Merge branch 'new-debian' into debian
authorBdale Garbee <bdale@gag.com>
Thu, 13 Sep 2012 02:01:22 +0000 (20:01 -0600)
committerBdale Garbee <bdale@gag.com>
Thu, 13 Sep 2012 02:01:22 +0000 (20:01 -0600)
Conflicts:
ChangeLog
debian/altos.install
debian/changelog
debian/control
debian/copyright
debian/dirs
debian/docs
debian/menu
debian/rules
src/Makefile

762 files changed:
.gitattributes [new file with mode: 0644]
.gitignore
ChangeLog
INSTALL
Makefile.am
README
Releasing [new file with mode: 0644]
altosdroid/.classpath [new file with mode: 0644]
altosdroid/.gitignore [new file with mode: 0644]
altosdroid/.project [new file with mode: 0644]
altosdroid/.settings/org.eclipse.jdt.core.prefs [new file with mode: 0644]
altosdroid/AndroidManifest.xml [new file with mode: 0644]
altosdroid/Makefile.am [new file with mode: 0644]
altosdroid/build.xml [new file with mode: 0644]
altosdroid/default.properties [new file with mode: 0644]
altosdroid/keystore [new file with mode: 0644]
altosdroid/libs/.gitignore [new file with mode: 0644]
altosdroid/local.properties.in [new file with mode: 0644]
altosdroid/project.properties [new file with mode: 0644]
altosdroid/res/drawable-hdpi/am_status_c.png [new file with mode: 0644]
altosdroid/res/drawable-hdpi/am_status_g.png [new file with mode: 0644]
altosdroid/res/drawable-hdpi/app_icon.png [new file with mode: 0644]
altosdroid/res/drawable-mdpi/am_status_c.png [new file with mode: 0644]
altosdroid/res/drawable-mdpi/am_status_g.png [new file with mode: 0644]
altosdroid/res/drawable/app_icon.png [new file with mode: 0644]
altosdroid/res/layout/altosdroid.xml [new file with mode: 0644]
altosdroid/res/layout/custom_title.xml [new file with mode: 0644]
altosdroid/res/layout/device_list.xml [new file with mode: 0644]
altosdroid/res/layout/device_name.xml [new file with mode: 0644]
altosdroid/res/layout/main.xml [new file with mode: 0644]
altosdroid/res/layout/message.xml [new file with mode: 0644]
altosdroid/res/menu/option_menu.xml [new file with mode: 0644]
altosdroid/res/values/strings.xml [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/Dumper.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java [new file with mode: 0644]
altoslib/.gitignore [new file with mode: 0644]
altoslib/AltosAccel.java [new file with mode: 0644]
altoslib/AltosCRCException.java [new file with mode: 0644]
altoslib/AltosConfigData.java [new file with mode: 0644]
altoslib/AltosConvert.java [new file with mode: 0644]
altoslib/AltosDistance.java [new file with mode: 0644]
altoslib/AltosEepromChunk.java [new file with mode: 0644]
altoslib/AltosEepromIterable.java [new file with mode: 0644]
altoslib/AltosEepromLog.java [new file with mode: 0644]
altoslib/AltosEepromMega.java [new file with mode: 0644]
altoslib/AltosEepromMegaIterable.java [new file with mode: 0644]
altoslib/AltosEepromRecord.java [new file with mode: 0644]
altoslib/AltosEepromTeleScience.java [new file with mode: 0644]
altoslib/AltosFile.java [new file with mode: 0644]
altoslib/AltosFlightReader.java [new file with mode: 0644]
altoslib/AltosFrequency.java [new file with mode: 0644]
altoslib/AltosGPS.java [new file with mode: 0644]
altoslib/AltosGPSQuery.java [new file with mode: 0644]
altoslib/AltosGPSSat.java [new file with mode: 0644]
altoslib/AltosGreatCircle.java [new file with mode: 0644]
altoslib/AltosHeight.java [new file with mode: 0644]
altoslib/AltosIMU.java [new file with mode: 0644]
altoslib/AltosIMUQuery.java [new file with mode: 0644]
altoslib/AltosIdleMonitor.java [new file with mode: 0644]
altoslib/AltosIdleMonitorListener.java [new file with mode: 0644]
altoslib/AltosIgnite.java [new file with mode: 0644]
altoslib/AltosLib.java [new file with mode: 0644]
altoslib/AltosLine.java [new file with mode: 0644]
altoslib/AltosLink.java [new file with mode: 0644]
altoslib/AltosLog.java [new file with mode: 0644]
altoslib/AltosMag.java [new file with mode: 0644]
altoslib/AltosMs5607.java [new file with mode: 0644]
altoslib/AltosMs5607Query.java [new file with mode: 0644]
altoslib/AltosOrderedMegaRecord.java [new file with mode: 0644]
altoslib/AltosOrderedRecord.java [new file with mode: 0644]
altoslib/AltosParse.java [new file with mode: 0644]
altoslib/AltosPreferences.java [new file with mode: 0644]
altoslib/AltosRecord.java [new file with mode: 0644]
altoslib/AltosRecordCompanion.java [new file with mode: 0644]
altoslib/AltosRecordIterable.java [new file with mode: 0644]
altoslib/AltosRecordMM.java [new file with mode: 0644]
altoslib/AltosRecordTM.java [new file with mode: 0644]
altoslib/AltosReplayReader.java [new file with mode: 0644]
altoslib/AltosSensorMM.java [new file with mode: 0644]
altoslib/AltosSensorTM.java [new file with mode: 0644]
altoslib/AltosSpeed.java [new file with mode: 0644]
altoslib/AltosState.java [new file with mode: 0644]
altoslib/AltosTelemetry.java [new file with mode: 0644]
altoslib/AltosTelemetryIterable.java [new file with mode: 0644]
altoslib/AltosTelemetryMap.java [new file with mode: 0644]
altoslib/AltosTelemetryReader.java [new file with mode: 0644]
altoslib/AltosTelemetryRecord.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordCompanion.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordConfiguration.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordGeneral.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordLegacy.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordLocation.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordMegaData.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordMegaSensor.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordRaw.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordSatellite.java [new file with mode: 0644]
altoslib/AltosTelemetryRecordSensor.java [new file with mode: 0644]
altoslib/AltosUnits.java [new file with mode: 0644]
altoslib/Makefile.am [new file with mode: 0644]
altosui/.gitignore [new file with mode: 0644]
altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml [new file with mode: 0644]
altosui/AltOS Package Configuration.pmdoc/01altosui.xml [new file with mode: 0644]
altosui/AltOS Package Configuration.pmdoc/index.xml [new file with mode: 0644]
altosui/Altos.java [new file with mode: 0644]
altosui/AltosAscent.java [new file with mode: 0644]
altosui/AltosBTDevice.java [new file with mode: 0644]
altosui/AltosBTDeviceIterator.java [new file with mode: 0644]
altosui/AltosBTKnown.java [new file with mode: 0644]
altosui/AltosBTManage.java [new file with mode: 0644]
altosui/AltosCSV.java [new file with mode: 0644]
altosui/AltosCSVUI.java [new file with mode: 0644]
altosui/AltosChannelMenu.java [new file with mode: 0644]
altosui/AltosCompanionInfo.java [new file with mode: 0644]
altosui/AltosConfig.java [new file with mode: 0644]
altosui/AltosConfigFreqUI.java [new file with mode: 0644]
altosui/AltosConfigTD.java [new file with mode: 0644]
altosui/AltosConfigTDUI.java [new file with mode: 0644]
altosui/AltosConfigUI.java [new file with mode: 0644]
altosui/AltosConfigureUI.java [new file with mode: 0644]
altosui/AltosDataChooser.java [new file with mode: 0644]
altosui/AltosDataPoint.java [new file with mode: 0644]
altosui/AltosDataPointReader.java [new file with mode: 0644]
altosui/AltosDebug.java [new file with mode: 0644]
altosui/AltosDescent.java [new file with mode: 0644]
altosui/AltosDevice.java [new file with mode: 0644]
altosui/AltosDeviceDialog.java [new file with mode: 0644]
altosui/AltosDialog.java [new file with mode: 0644]
altosui/AltosDisplayThread.java [new file with mode: 0644]
altosui/AltosEepromDelete.java [new file with mode: 0644]
altosui/AltosEepromDownload.java [new file with mode: 0644]
altosui/AltosEepromList.java [new file with mode: 0644]
altosui/AltosEepromManage.java [new file with mode: 0644]
altosui/AltosEepromMonitor.java [new file with mode: 0644]
altosui/AltosEepromSelect.java [new file with mode: 0644]
altosui/AltosFlash.java [new file with mode: 0644]
altosui/AltosFlashUI.java [new file with mode: 0644]
altosui/AltosFlightDisplay.java [new file with mode: 0644]
altosui/AltosFlightInfoTableModel.java [new file with mode: 0644]
altosui/AltosFlightStats.java [new file with mode: 0644]
altosui/AltosFlightStatsTable.java [new file with mode: 0644]
altosui/AltosFlightStatus.java [new file with mode: 0644]
altosui/AltosFlightStatusTableModel.java [new file with mode: 0644]
altosui/AltosFlightStatusUpdate.java [new file with mode: 0644]
altosui/AltosFlightUI.java [new file with mode: 0644]
altosui/AltosFontListener.java [new file with mode: 0644]
altosui/AltosFrame.java [new file with mode: 0644]
altosui/AltosFreqList.java [new file with mode: 0644]
altosui/AltosGraph.java [new file with mode: 0644]
altosui/AltosGraphTime.java [new file with mode: 0644]
altosui/AltosGraphUI.java [new file with mode: 0644]
altosui/AltosHexfile.java [new file with mode: 0644]
altosui/AltosIdleMonitorUI.java [new file with mode: 0644]
altosui/AltosIgniteUI.java [new file with mode: 0644]
altosui/AltosInfoTable.java [new file with mode: 0644]
altosui/AltosKML.java [new file with mode: 0644]
altosui/AltosLanded.java [new file with mode: 0644]
altosui/AltosLaunch.java [new file with mode: 0644]
altosui/AltosLaunchUI.java [new file with mode: 0644]
altosui/AltosLed.java [new file with mode: 0644]
altosui/AltosLights.java [new file with mode: 0644]
altosui/AltosPad.java [new file with mode: 0644]
altosui/AltosRomconfig.java [new file with mode: 0644]
altosui/AltosRomconfigUI.java [new file with mode: 0644]
altosui/AltosScanUI.java [new file with mode: 0644]
altosui/AltosSerial.java [new file with mode: 0644]
altosui/AltosSerialInUseException.java [new file with mode: 0644]
altosui/AltosSiteMap.java [new file with mode: 0644]
altosui/AltosSiteMapCache.java [new file with mode: 0644]
altosui/AltosSiteMapPreload.java [new file with mode: 0644]
altosui/AltosSiteMapTile.java [new file with mode: 0644]
altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub [new file with mode: 0755]
altosui/AltosUI.app/Contents/PkgInfo [new file with mode: 0644]
altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns [new file with mode: 0644]
altosui/AltosUI.java [new file with mode: 0644]
altosui/AltosUIListener.java [new file with mode: 0644]
altosui/AltosUIPreferences.java [new file with mode: 0644]
altosui/AltosUSBDevice.java [new file with mode: 0644]
altosui/AltosVersion.java.in [new file with mode: 0644]
altosui/AltosVoice.java [new file with mode: 0644]
altosui/AltosWriter.java [new file with mode: 0644]
altosui/GrabNDrag.java [new file with mode: 0644]
altosui/Info.plist.in [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf [new file with mode: 0644]
altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys [new file with mode: 0644]
altosui/Instdrv/NSIS/Plugins/InstDrv.dll [new file with mode: 0644]
altosui/Makefile-standalone [new file with mode: 0644]
altosui/Makefile.am [new file with mode: 0644]
altosui/altos-windows.nsi [new file with mode: 0644]
altosui/altosui-fat [new file with mode: 0755]
altosui/altosui.1 [new file with mode: 0644]
altosui/altusmetrum.jpg [new file with mode: 0644]
altosui/launch-sites.txt [new file with mode: 0644]
altosui/libaltos/.gitignore [new file with mode: 0644]
altosui/libaltos/Makefile-standalone [new file with mode: 0644]
altosui/libaltos/Makefile.am [new file with mode: 0644]
altosui/libaltos/cjnitest.c [new file with mode: 0644]
altosui/libaltos/libaltos.c [new file with mode: 0644]
altosui/libaltos/libaltos.dylib [new file with mode: 0755]
altosui/libaltos/libaltos.h [new file with mode: 0644]
altosui/libaltos/libaltos.i0 [new file with mode: 0644]
ao-bringup/.gitignore [new file with mode: 0644]
ao-bringup/Makefile [new file with mode: 0644]
ao-bringup/ao_bringup.h [new file with mode: 0644]
ao-bringup/ao_init.c [new file with mode: 0644]
ao-bringup/ao_led_blink.c [new file with mode: 0644]
ao-bringup/ao_radio_init.c [new file with mode: 0644]
ao-bringup/ao_radio_test.c [new file with mode: 0644]
ao-bringup/ao_radio_xmit.c [new file with mode: 0644]
ao-bringup/megametrum.cfg [new file with mode: 0644]
ao-bringup/megametrum.gdb [new file with mode: 0644]
ao-bringup/testplan [new file with mode: 0644]
ao-bringup/turnon_telebt [new file with mode: 0755]
ao-bringup/turnon_teledongle [new file with mode: 0755]
ao-bringup/turnon_telemetrum [new file with mode: 0755]
ao-bringup/turnon_telemini [new file with mode: 0755]
ao-bringup/turnon_teleshield [new file with mode: 0755]
ao-bringup/turnon_teleterra [new file with mode: 0755]
ao-tools/Makefile.am
ao-tools/ao-dbg/ao-dbg-main.c
ao-tools/ao-dbg/ao-dbg-parse.c
ao-tools/ao-dbg/ao-dbg.1
ao-tools/ao-dbg/ao-dbg.h
ao-tools/ao-dumplog/Makefile.am [new file with mode: 0644]
ao-tools/ao-dumplog/ao-dumplog.1 [new file with mode: 0644]
ao-tools/ao-dumplog/ao-dumplog.c [new file with mode: 0644]
ao-tools/ao-eeprom/ao-eeprom.1
ao-tools/ao-eeprom/ao-eeprom.c
ao-tools/ao-list/Makefile.am [new file with mode: 0644]
ao-tools/ao-list/ao-list.1 [new file with mode: 0644]
ao-tools/ao-list/ao-list.c [new file with mode: 0644]
ao-tools/ao-load/ao-load.1
ao-tools/ao-load/ao-load.c
ao-tools/ao-postflight/Makefile.am [new file with mode: 0644]
ao-tools/ao-postflight/ao-postflight.1 [new file with mode: 0644]
ao-tools/ao-postflight/ao-postflight.c [new file with mode: 0644]
ao-tools/ao-rawload/ao-rawload.1
ao-tools/ao-rawload/ao-rawload.c
ao-tools/ao-send-telem/Makefile.am [new file with mode: 0644]
ao-tools/ao-send-telem/ao-send-telem.1 [new file with mode: 0644]
ao-tools/ao-send-telem/ao-send-telem.c [new file with mode: 0644]
ao-tools/ao-stmload/.gitignore [new file with mode: 0644]
ao-tools/ao-stmload/Makefile.am [new file with mode: 0644]
ao-tools/ao-stmload/ao-stmload.1 [new file with mode: 0644]
ao-tools/ao-stmload/ao-stmload.c [new file with mode: 0644]
ao-tools/ao-telem/.gitignore [new file with mode: 0644]
ao-tools/ao-telem/Makefile.am [new file with mode: 0644]
ao-tools/ao-telem/ao-telem.1 [new file with mode: 0644]
ao-tools/ao-telem/ao-telem.c [new file with mode: 0644]
ao-tools/ao-view/.gitignore [new file with mode: 0644]
ao-tools/ao-view/Makefile.am [new file with mode: 0644]
ao-tools/ao-view/ao-view.1 [new file with mode: 0644]
ao-tools/ao-view/aoview.glade [new file with mode: 0644]
ao-tools/ao-view/aoview.h [new file with mode: 0644]
ao-tools/ao-view/aoview_channel.c [new file with mode: 0644]
ao-tools/ao-view/aoview_convert.c [new file with mode: 0644]
ao-tools/ao-view/aoview_dev_dialog.c [new file with mode: 0644]
ao-tools/ao-view/aoview_eeprom.c [new file with mode: 0644]
ao-tools/ao-view/aoview_file.c [new file with mode: 0644]
ao-tools/ao-view/aoview_flite.c [new file with mode: 0644]
ao-tools/ao-view/aoview_label.c [new file with mode: 0644]
ao-tools/ao-view/aoview_log.c [new file with mode: 0644]
ao-tools/ao-view/aoview_main.c [new file with mode: 0644]
ao-tools/ao-view/aoview_monitor.c [new file with mode: 0644]
ao-tools/ao-view/aoview_replay.c [new file with mode: 0644]
ao-tools/ao-view/aoview_serial.c [new file with mode: 0644]
ao-tools/ao-view/aoview_state.c [new file with mode: 0644]
ao-tools/ao-view/aoview_table.c [new file with mode: 0644]
ao-tools/ao-view/aoview_util.c [new file with mode: 0644]
ao-tools/ao-view/aoview_voice.c [new file with mode: 0644]
ao-tools/ao-view/design [new file with mode: 0644]
ao-tools/lib/Makefile.am
ao-tools/lib/cc-analyse.c [new file with mode: 0644]
ao-tools/lib/cc-convert.c [new file with mode: 0644]
ao-tools/lib/cc-dsp.c [new file with mode: 0644]
ao-tools/lib/cc-integrate.c [new file with mode: 0644]
ao-tools/lib/cc-log.c [new file with mode: 0644]
ao-tools/lib/cc-logfile.c [new file with mode: 0644]
ao-tools/lib/cc-period.c [new file with mode: 0644]
ao-tools/lib/cc-process.c [new file with mode: 0644]
ao-tools/lib/cc-telem.c [new file with mode: 0644]
ao-tools/lib/cc-telemetry.c [new file with mode: 0644]
ao-tools/lib/cc-telemetry.h [new file with mode: 0644]
ao-tools/lib/cc-usb.c
ao-tools/lib/cc-usb.h
ao-tools/lib/cc-usbdev.c [new file with mode: 0644]
ao-tools/lib/cc-util.c [new file with mode: 0644]
ao-tools/lib/cc.h [new file with mode: 0644]
ao-tools/lib/cephes.h [new file with mode: 0644]
ao-tools/lib/chbevl.c [new file with mode: 0644]
ao-tools/lib/cmath.h [new file with mode: 0644]
ao-tools/lib/i0.c [new file with mode: 0644]
ao-tools/lib/mconf.h [new file with mode: 0644]
ao-tools/tests/reset
ao-view/.gitignore [deleted file]
ao-view/Makefile.am [deleted file]
ao-view/ao-view.1 [deleted file]
ao-view/aoview.glade [deleted file]
ao-view/aoview.h [deleted file]
ao-view/aoview_convert.c [deleted file]
ao-view/aoview_dev.c [deleted file]
ao-view/aoview_dev_dialog.c [deleted file]
ao-view/aoview_eeprom.c [deleted file]
ao-view/aoview_file.c [deleted file]
ao-view/aoview_flite.c [deleted file]
ao-view/aoview_label.c [deleted file]
ao-view/aoview_log.c [deleted file]
ao-view/aoview_main.c [deleted file]
ao-view/aoview_monitor.c [deleted file]
ao-view/aoview_replay.c [deleted file]
ao-view/aoview_serial.c [deleted file]
ao-view/aoview_state.c [deleted file]
ao-view/aoview_table.c [deleted file]
ao-view/aoview_util.c [deleted file]
ao-view/aoview_voice.c [deleted file]
ao-view/design [deleted file]
configure.ac
contrib/arch-linux/PKGBUILD-git.altos [new file with mode: 0644]
contrib/arch-linux/PKGBUILD-git.freetts [new file with mode: 0644]
contrib/arch-linux/PKGBUILD-git.jcommon [new file with mode: 0644]
contrib/arch-linux/PKGBUILD-git.jfreechart [new file with mode: 0644]
contrib/arch-linux/PKGBUILD-git.nickle [new file with mode: 0644]
contrib/arch-linux/PKGBUILD-git.nsis.patched [new file with mode: 0644]
contrib/arch-linux/PKGBUILD-git.sdcc_patched [new file with mode: 0644]
contrib/arch-linux/README [new file with mode: 0644]
contrib/arch-linux/new.patch [new file with mode: 0644]
contrib/arch-linux/nsis-2.43-64bit-fixes.patch [new file with mode: 0644]
debian/.gitignore [new file with mode: 0644]
debian/altos.desktop [new file with mode: 0644]
debian/altos.install
debian/altos.postinst [new file with mode: 0644]
debian/altusmetrum.xpm [new file with mode: 0644]
debian/changelog
debian/control
debian/copyright
debian/dirs
debian/docs
debian/menu
debian/rules
debian/source/format [new file with mode: 0644]
doc/.gitignore [new file with mode: 0644]
doc/Makefile [new file with mode: 0644]
doc/altos.xsl [new file with mode: 0644]
doc/altusmetrum.xsl [new file with mode: 0644]
doc/companion.xsl [new file with mode: 0644]
doc/megametrum-outline.pdf [new file with mode: 0644]
doc/megametrum-outline.svg [new file with mode: 0644]
doc/release-notes-0.7.1.xsl [new file with mode: 0644]
doc/release-notes-0.8.xsl [new file with mode: 0644]
doc/release-notes-0.9.2.xsl [new file with mode: 0644]
doc/release-notes-0.9.xsl [new file with mode: 0644]
doc/release-notes-1.0.1.xsl [new file with mode: 0644]
doc/release-notes-1.1.xsl [new file with mode: 0644]
doc/telemetrum-outline.pdf [new file with mode: 0644]
doc/telemetrum-outline.svg [new file with mode: 0644]
doc/telemetry.xsl [new file with mode: 0644]
icon/altus-metrum-16x16.jpg [new file with mode: 0644]
icon/altus-metrum.ico [new file with mode: 0644]
icon/grayled.png [new file with mode: 0644]
icon/grayon.png [new file with mode: 0644]
icon/greenled.png [new file with mode: 0644]
icon/greenoff.png [new file with mode: 0644]
icon/redled.png [new file with mode: 0644]
icon/redoff.png [new file with mode: 0644]
src/.gitignore [new file with mode: 0644]
src/25lc1024.h [deleted file]
src/Makefile
src/Version.in [new file with mode: 0644]
src/_bp.c [deleted file]
src/aes/ao_aes.c [new file with mode: 0644]
src/aes/ao_aes_int.h [new file with mode: 0644]
src/aes/ao_aes_tables.c [new file with mode: 0644]
src/aes/ao_aes_tables.h [new file with mode: 0644]
src/altitude.h [deleted file]
src/ao-make-product.5c [deleted file]
src/ao.h [deleted file]
src/ao_adc.c [deleted file]
src/ao_adc_fake.c [deleted file]
src/ao_beep.c [deleted file]
src/ao_cmd.c [deleted file]
src/ao_config.c [deleted file]
src/ao_convert.c [deleted file]
src/ao_dbg.c [deleted file]
src/ao_dma.c [deleted file]
src/ao_ee.c [deleted file]
src/ao_ee_fake.c [deleted file]
src/ao_flight.c [deleted file]
src/ao_flight_test.c [deleted file]
src/ao_gps.c [deleted file]
src/ao_gps_print.c [deleted file]
src/ao_gps_report.c [deleted file]
src/ao_gps_test.c [deleted file]
src/ao_host.h [deleted file]
src/ao_ignite.c [deleted file]
src/ao_led.c [deleted file]
src/ao_log.c [deleted file]
src/ao_main.c [deleted file]
src/ao_monitor.c [deleted file]
src/ao_mutex.c [deleted file]
src/ao_panic.c [deleted file]
src/ao_product.c [deleted file]
src/ao_radio.c [deleted file]
src/ao_report.c [deleted file]
src/ao_rssi.c [deleted file]
src/ao_serial.c [deleted file]
src/ao_state.c [deleted file]
src/ao_stdio.c [deleted file]
src/ao_task.c [deleted file]
src/ao_teledongle.c [deleted file]
src/ao_telemetrum.c [deleted file]
src/ao_telemetry.c [deleted file]
src/ao_teleterra.c [deleted file]
src/ao_test.c [deleted file]
src/ao_tidongle.c [deleted file]
src/ao_timer.c [deleted file]
src/ao_usb.c [deleted file]
src/ao_usb.h [deleted file]
src/avr-demo/.gitignore [new file with mode: 0644]
src/avr-demo/Makefile [new file with mode: 0644]
src/avr-demo/ao_demo.c [new file with mode: 0644]
src/avr/ao_adc_avr.c [new file with mode: 0644]
src/avr/ao_arch.h [new file with mode: 0644]
src/avr/ao_arch_funcs.h [new file with mode: 0644]
src/avr/ao_avr_stdio.c [new file with mode: 0644]
src/avr/ao_clock.c [new file with mode: 0644]
src/avr/ao_debug_avr.c [new file with mode: 0644]
src/avr/ao_eeprom_avr.c [new file with mode: 0644]
src/avr/ao_i2c_usart.c [new file with mode: 0644]
src/avr/ao_lcd_port.c [new file with mode: 0644]
src/avr/ao_led.c [new file with mode: 0644]
src/avr/ao_pins.h [new file with mode: 0644]
src/avr/ao_romconfig.c [new file with mode: 0644]
src/avr/ao_serial_avr.c [new file with mode: 0644]
src/avr/ao_spi_slave.c [new file with mode: 0644]
src/avr/ao_spi_usart.c [new file with mode: 0644]
src/avr/ao_timer.c [new file with mode: 0644]
src/avr/ao_usb_avr.c [new file with mode: 0644]
src/cc1111.h [deleted file]
src/cc1111/Makefile.cc1111 [new file with mode: 0644]
src/cc1111/_bp.c [new file with mode: 0644]
src/cc1111/ao_adc.c [new file with mode: 0644]
src/cc1111/ao_aes.c [new file with mode: 0644]
src/cc1111/ao_arch.h [new file with mode: 0644]
src/cc1111/ao_arch_funcs.h [new file with mode: 0644]
src/cc1111/ao_battery.c [new file with mode: 0644]
src/cc1111/ao_beep.c [new file with mode: 0644]
src/cc1111/ao_button.c [new file with mode: 0644]
src/cc1111/ao_dbg.c [new file with mode: 0644]
src/cc1111/ao_dma.c [new file with mode: 0644]
src/cc1111/ao_intflash.c [new file with mode: 0644]
src/cc1111/ao_launch.c [new file with mode: 0644]
src/cc1111/ao_lcd_port.c [new file with mode: 0644]
src/cc1111/ao_led.c [new file with mode: 0644]
src/cc1111/ao_pins.h [new file with mode: 0644]
src/cc1111/ao_radio.c [new file with mode: 0644]
src/cc1111/ao_reboot.c [new file with mode: 0644]
src/cc1111/ao_romconfig.c [new file with mode: 0644]
src/cc1111/ao_serial.c [new file with mode: 0644]
src/cc1111/ao_spi.c [new file with mode: 0644]
src/cc1111/ao_string.c [new file with mode: 0644]
src/cc1111/ao_timer.c [new file with mode: 0644]
src/cc1111/ao_usb.c [new file with mode: 0644]
src/cc1111/cc1111.h [new file with mode: 0644]
src/check-stack [deleted file]
src/core/altitude.h [new file with mode: 0644]
src/core/ao.h [new file with mode: 0644]
src/core/ao_adc.h [new file with mode: 0644]
src/core/ao_aes.h [new file with mode: 0644]
src/core/ao_beep.h [new file with mode: 0644]
src/core/ao_btm.h [new file with mode: 0644]
src/core/ao_cmd.c [new file with mode: 0644]
src/core/ao_companion.h [new file with mode: 0644]
src/core/ao_config.c [new file with mode: 0644]
src/core/ao_convert.c [new file with mode: 0644]
src/core/ao_convert_pa.c [new file with mode: 0644]
src/core/ao_convert_pa_test.c [new file with mode: 0644]
src/core/ao_convert_test.c [new file with mode: 0644]
src/core/ao_data.h [new file with mode: 0644]
src/core/ao_dbg.h [new file with mode: 0644]
src/core/ao_ee_fake.c [new file with mode: 0644]
src/core/ao_fec.h [new file with mode: 0644]
src/core/ao_fec_rx.c [new file with mode: 0644]
src/core/ao_fec_tx.c [new file with mode: 0644]
src/core/ao_flight.c [new file with mode: 0644]
src/core/ao_flight.h [new file with mode: 0644]
src/core/ao_flight_nano.c [new file with mode: 0644]
src/core/ao_freq.c [new file with mode: 0644]
src/core/ao_gps_print.c [new file with mode: 0644]
src/core/ao_gps_report.c [new file with mode: 0644]
src/core/ao_gps_report_mega.c [new file with mode: 0644]
src/core/ao_host.h [new file with mode: 0644]
src/core/ao_ignite.c [new file with mode: 0644]
src/core/ao_kalman.c [new file with mode: 0644]
src/core/ao_lcd.h [new file with mode: 0644]
src/core/ao_led.h [new file with mode: 0644]
src/core/ao_log.c [new file with mode: 0644]
src/core/ao_log.h [new file with mode: 0644]
src/core/ao_log_big.c [new file with mode: 0644]
src/core/ao_log_mega.c [new file with mode: 0644]
src/core/ao_log_single.c [new file with mode: 0644]
src/core/ao_log_telem.c [new file with mode: 0644]
src/core/ao_log_telescience.c [new file with mode: 0644]
src/core/ao_log_tiny.c [new file with mode: 0644]
src/core/ao_monitor.c [new file with mode: 0644]
src/core/ao_mutex.c [new file with mode: 0644]
src/core/ao_packet.h [new file with mode: 0644]
src/core/ao_panic.c [new file with mode: 0644]
src/core/ao_product.c [new file with mode: 0644]
src/core/ao_pyro.c [new file with mode: 0644]
src/core/ao_pyro.h [new file with mode: 0644]
src/core/ao_radio_cmac.c [new file with mode: 0644]
src/core/ao_radio_cmac.h [new file with mode: 0644]
src/core/ao_radio_cmac_cmd.c [new file with mode: 0644]
src/core/ao_radio_cmac_cmd.h [new file with mode: 0644]
src/core/ao_report.c [new file with mode: 0644]
src/core/ao_rssi.c [new file with mode: 0644]
src/core/ao_sample.c [new file with mode: 0644]
src/core/ao_sample.h [new file with mode: 0644]
src/core/ao_send_packet.c [new file with mode: 0644]
src/core/ao_send_packet.h [new file with mode: 0644]
src/core/ao_serial.h [new file with mode: 0644]
src/core/ao_sqrt.c [new file with mode: 0644]
src/core/ao_state.c [new file with mode: 0644]
src/core/ao_stdio.c [new file with mode: 0644]
src/core/ao_storage.c [new file with mode: 0644]
src/core/ao_storage.h [new file with mode: 0644]
src/core/ao_task.c [new file with mode: 0644]
src/core/ao_telem.h [new file with mode: 0644]
src/core/ao_telemetry.c [new file with mode: 0644]
src/core/ao_telemetry.h [new file with mode: 0644]
src/core/ao_usb.h [new file with mode: 0644]
src/drivers/ao_25lc1024.c [new file with mode: 0644]
src/drivers/ao_25lc1024.h [new file with mode: 0644]
src/drivers/ao_74hc497.c [new file with mode: 0644]
src/drivers/ao_74hc497.h [new file with mode: 0644]
src/drivers/ao_at45db161d.c [new file with mode: 0644]
src/drivers/ao_at45db161d.h [new file with mode: 0644]
src/drivers/ao_btm.c [new file with mode: 0644]
src/drivers/ao_button.c [new file with mode: 0644]
src/drivers/ao_button.h [new file with mode: 0644]
src/drivers/ao_cc1120.c [new file with mode: 0644]
src/drivers/ao_cc1120.h [new file with mode: 0644]
src/drivers/ao_cc1120_CC1120.h [new file with mode: 0644]
src/drivers/ao_companion.c [new file with mode: 0644]
src/drivers/ao_event.c [new file with mode: 0644]
src/drivers/ao_event.h [new file with mode: 0644]
src/drivers/ao_gps_sirf.c [new file with mode: 0644]
src/drivers/ao_gps_skytraq.c [new file with mode: 0644]
src/drivers/ao_hmc5883.c [new file with mode: 0644]
src/drivers/ao_hmc5883.h [new file with mode: 0644]
src/drivers/ao_lcd.c [new file with mode: 0644]
src/drivers/ao_lco_cmd.c [new file with mode: 0644]
src/drivers/ao_lco_cmd.h [new file with mode: 0644]
src/drivers/ao_lco_func.c [new file with mode: 0644]
src/drivers/ao_lco_func.h [new file with mode: 0644]
src/drivers/ao_m25.c [new file with mode: 0644]
src/drivers/ao_mma655x.c [new file with mode: 0644]
src/drivers/ao_mma655x.h [new file with mode: 0644]
src/drivers/ao_mpu6000.c [new file with mode: 0644]
src/drivers/ao_mpu6000.h [new file with mode: 0644]
src/drivers/ao_ms5607.c [new file with mode: 0644]
src/drivers/ao_ms5607.h [new file with mode: 0644]
src/drivers/ao_packet.c [new file with mode: 0644]
src/drivers/ao_packet_master.c [new file with mode: 0644]
src/drivers/ao_packet_slave.c [new file with mode: 0644]
src/drivers/ao_pad.c [new file with mode: 0644]
src/drivers/ao_pad.h [new file with mode: 0644]
src/drivers/ao_pca9922.c [new file with mode: 0644]
src/drivers/ao_pyro_slave.c [new file with mode: 0644]
src/drivers/ao_quadrature.c [new file with mode: 0644]
src/drivers/ao_quadrature.h [new file with mode: 0644]
src/drivers/ao_radio_master.c [new file with mode: 0644]
src/drivers/ao_radio_slave.c [new file with mode: 0644]
src/drivers/ao_radio_spi.h [new file with mode: 0644]
src/drivers/ao_science_slave.c [new file with mode: 0644]
src/drivers/ao_seven_segment.c [new file with mode: 0644]
src/drivers/ao_seven_segment.h [new file with mode: 0644]
src/gps-cksum [deleted file]
src/kalman/kalman.5c [new file with mode: 0755]
src/kalman/kalman_filter.5c [new file with mode: 0644]
src/kalman/load_csv.5c [new file with mode: 0644]
src/kalman/matrix.5c [new file with mode: 0644]
src/kalman/plotaccel [new file with mode: 0644]
src/kalman/plotkalman [new file with mode: 0755]
src/make-altitude [deleted file]
src/megametrum-v0.1/.gitignore [new file with mode: 0644]
src/megametrum-v0.1/Makefile [new file with mode: 0644]
src/megametrum-v0.1/ao_megametrum.c [new file with mode: 0644]
src/megametrum-v0.1/ao_pins.h [new file with mode: 0644]
src/megametrum-v0.1/stlink-pins [new file with mode: 0644]
src/product/Makefile.telebt [new file with mode: 0644]
src/product/Makefile.teledongle [new file with mode: 0644]
src/product/Makefile.telelaunch [new file with mode: 0644]
src/product/Makefile.telemetrum [new file with mode: 0644]
src/product/Makefile.telemini [new file with mode: 0644]
src/product/Makefile.telenano [new file with mode: 0644]
src/product/ao_telebt.c [new file with mode: 0644]
src/product/ao_teledongle.c [new file with mode: 0644]
src/product/ao_telelaunch.c [new file with mode: 0644]
src/product/ao_telemetrum.c [new file with mode: 0644]
src/product/ao_telemini.c [new file with mode: 0644]
src/product/ao_telenano.c [new file with mode: 0644]
src/product/ao_telepyro.c [new file with mode: 0644]
src/product/ao_telescience.c [new file with mode: 0644]
src/product/ao_teleterra.c [new file with mode: 0644]
src/product/ao_teleterra_0_2.c [new file with mode: 0644]
src/product/ao_terraui.c [new file with mode: 0644]
src/product/ao_test.c [new file with mode: 0644]
src/product/ao_tidongle.c [new file with mode: 0644]
src/sirf-cksum [deleted file]
src/spiradio-v0.1/.gitignore [new file with mode: 0644]
src/spiradio-v0.1/.sdcdbrc [new file with mode: 0644]
src/spiradio-v0.1/Makefile [new file with mode: 0644]
src/spiradio-v0.1/ao_pins.h [new file with mode: 0644]
src/spiradio-v0.1/ao_spiradio.c [new file with mode: 0644]
src/stm-bringup/.gitignore [new file with mode: 0644]
src/stm-bringup/Makefile [new file with mode: 0644]
src/stm-bringup/bringup.c [new file with mode: 0644]
src/stm-bringup/bringup.ld [new file with mode: 0644]
src/stm-demo/.gitignore [new file with mode: 0644]
src/stm-demo/Makefile [new file with mode: 0644]
src/stm-demo/ao_demo.c [new file with mode: 0644]
src/stm-demo/ao_pins.h [new file with mode: 0644]
src/stm/Makefile.defs [new file with mode: 0644]
src/stm/altos-ram.ld [new file with mode: 0644]
src/stm/altos.ld [new file with mode: 0644]
src/stm/ao-parse-font.5c [new file with mode: 0644]
src/stm/ao_adc_stm.c [new file with mode: 0644]
src/stm/ao_arch.h [new file with mode: 0644]
src/stm/ao_arch_funcs.h [new file with mode: 0644]
src/stm/ao_beep_stm.c [new file with mode: 0644]
src/stm/ao_data.c [new file with mode: 0644]
src/stm/ao_dma_stm.c [new file with mode: 0644]
src/stm/ao_eeprom_stm.c [new file with mode: 0644]
src/stm/ao_exti.h [new file with mode: 0644]
src/stm/ao_exti_stm.c [new file with mode: 0644]
src/stm/ao_i2c_stm.c [new file with mode: 0644]
src/stm/ao_interrupt.c [new file with mode: 0644]
src/stm/ao_lcd_font.c [new file with mode: 0644]
src/stm/ao_lcd_font.h [new file with mode: 0644]
src/stm/ao_lcd_stm.c [new file with mode: 0644]
src/stm/ao_lcd_stm.h [new file with mode: 0644]
src/stm/ao_led.c [new file with mode: 0644]
src/stm/ao_profile.c [new file with mode: 0644]
src/stm/ao_profile.h [new file with mode: 0644]
src/stm/ao_romconfig.c [new file with mode: 0644]
src/stm/ao_serial_stm.c [new file with mode: 0644]
src/stm/ao_spi_stm.c [new file with mode: 0644]
src/stm/ao_timer.c [new file with mode: 0644]
src/stm/ao_usb_stm.c [new file with mode: 0644]
src/stm/registers.ld [new file with mode: 0644]
src/stm/stm32l.h [new file with mode: 0644]
src/teleballoon-v1.1/.gitignore [new file with mode: 0644]
src/teleballoon-v1.1/Makefile [new file with mode: 0644]
src/teleballoon-v1.1/ao_balloon.c [new file with mode: 0644]
src/teleballoon-v1.1/ao_pins.h [new file with mode: 0644]
src/teleballoon-v1.1/ao_teleballoon.c [new file with mode: 0644]
src/telebt-v0.0/.gitignore [new file with mode: 0644]
src/telebt-v0.0/.sdcdbrc [new file with mode: 0644]
src/telebt-v0.0/Makefile [new file with mode: 0644]
src/telebt-v0.1/.gitignore [new file with mode: 0644]
src/telebt-v0.1/.sdcdbrc [new file with mode: 0644]
src/telebt-v0.1/Makefile [new file with mode: 0644]
src/teledongle-v0.1/.gitignore [new file with mode: 0644]
src/teledongle-v0.1/.sdcdbrc [new file with mode: 0644]
src/teledongle-v0.1/Makefile [new file with mode: 0644]
src/teledongle-v0.2/.gitignore [new file with mode: 0644]
src/teledongle-v0.2/.sdcdbrc [new file with mode: 0644]
src/teledongle-v0.2/Makefile [new file with mode: 0644]
src/telefire-v0.1/.gitignore [new file with mode: 0644]
src/telefire-v0.1/.sdcdbrc [new file with mode: 0644]
src/telefire-v0.1/Makefile [new file with mode: 0644]
src/telefire-v0.1/ao_pins.h [new file with mode: 0644]
src/telefire-v0.1/ao_telefire.c [new file with mode: 0644]
src/telelaunch-v0.1/.gitignore [new file with mode: 0644]
src/telelaunch-v0.1/.sdcdbrc [new file with mode: 0644]
src/telelaunch-v0.1/Makefile [new file with mode: 0644]
src/telelaunch-v0.1/Makefile.defs [new file with mode: 0644]
src/telelco-v0.1/.gitignore [new file with mode: 0644]
src/telelco-v0.1/Makefile [new file with mode: 0644]
src/telelco-v0.1/ao_lco.c [new file with mode: 0644]
src/telelco-v0.1/ao_lco.h [new file with mode: 0644]
src/telelco-v0.1/ao_pins.h [new file with mode: 0644]
src/telelco-v0.1/ao_telelco.c [new file with mode: 0644]
src/telemetrum-v0.1-sirf/.gitignore [new file with mode: 0644]
src/telemetrum-v0.1-sirf/Makefile [new file with mode: 0644]
src/telemetrum-v0.1-sky/.gitignore [new file with mode: 0644]
src/telemetrum-v0.1-sky/.sdcdbrc [new file with mode: 0644]
src/telemetrum-v0.1-sky/Makefile [new file with mode: 0644]
src/telemetrum-v1.0/.gitignore [new file with mode: 0644]
src/telemetrum-v1.0/.sdcdbrc [new file with mode: 0644]
src/telemetrum-v1.0/Makefile [new file with mode: 0644]
src/telemetrum-v1.1/.gitignore [new file with mode: 0644]
src/telemetrum-v1.1/.sdcdbrc [new file with mode: 0644]
src/telemetrum-v1.1/Makefile [new file with mode: 0644]
src/telemetrum-v1.2/.gitignore [new file with mode: 0644]
src/telemetrum-v1.2/.sdcdbrc [new file with mode: 0644]
src/telemetrum-v1.2/Makefile [new file with mode: 0644]
src/telemini-v1.0/.gitignore [new file with mode: 0644]
src/telemini-v1.0/.sdcdbrc [new file with mode: 0644]
src/telemini-v1.0/Makefile [new file with mode: 0644]
src/telenano-v0.1/.gitignore [new file with mode: 0644]
src/telenano-v0.1/.sdcdbrc [new file with mode: 0644]
src/telenano-v0.1/Makefile [new file with mode: 0644]
src/telepyro-v0.1/.gitignore [new file with mode: 0644]
src/telepyro-v0.1/Makefile [new file with mode: 0644]
src/telescience-v0.1/.gitignore [new file with mode: 0644]
src/telescience-v0.1/Makefile [new file with mode: 0644]
src/teleshield-v0.1/.gitignore [new file with mode: 0644]
src/teleshield-v0.1/Makefile [new file with mode: 0644]
src/teleshield-v0.1/ao_ardu_serial.c [new file with mode: 0644]
src/teleshield-v0.1/ao_pins.h [new file with mode: 0644]
src/teleshield-v0.1/ao_teleshield.c [new file with mode: 0644]
src/teleterra-v0.1/.gitignore [new file with mode: 0644]
src/teleterra-v0.1/ao_pins.h [new file with mode: 0644]
src/teleterra-v0.2/.gitignore [new file with mode: 0644]
src/teleterra-v0.2/.sdcdbrc [new file with mode: 0644]
src/teleterra-v0.2/Makefile [new file with mode: 0644]
src/teleterra-v0.2/ao_pins.h [new file with mode: 0644]
src/test/.gitignore [new file with mode: 0644]
src/test/Makefile [new file with mode: 0644]
src/test/ao_fec_test.c [new file with mode: 0644]
src/test/ao_flight_test.c [new file with mode: 0644]
src/test/ao_gps_test.c [new file with mode: 0644]
src/test/ao_gps_test_skytraq.c [new file with mode: 0644]
src/test/plottest [new file with mode: 0755]
src/test/run-baro [new file with mode: 0755]
src/test/run-full [new file with mode: 0755]
src/test/run-noisy [new file with mode: 0755]
src/test/run-one [new file with mode: 0755]
src/test/run-tests [new file with mode: 0755]
src/test/test-flights [new file with mode: 0644]
src/tidongle/.gitignore [new file with mode: 0644]
src/tidongle/Makefile [new file with mode: 0644]
src/util/ao-make-product.5c [new file with mode: 0644]
src/util/check-avr-mem [new file with mode: 0644]
src/util/check-stack [new file with mode: 0755]
src/util/gps-cksum [new file with mode: 0755]
src/util/make-altitude [new file with mode: 0644]
src/util/make-altitude-pa [new file with mode: 0644]
src/util/make-kalman [new file with mode: 0644]
src/util/make-whiten [new file with mode: 0644]
src/util/sirf-cksum [new file with mode: 0755]
src/util/skytraq-cksum [new file with mode: 0644]
telemetrum.inf [new file with mode: 0755]
themes/background.png [new file with mode: 0644]
themes/background.xcf [new file with mode: 0644]
themes/gdm/GdmGreeterTheme.desktop [new file with mode: 0644]
themes/gdm/altusmetrum.xml [new file with mode: 0644]
themes/gdm/screenshot.png [new file with mode: 0644]
themes/slim/panel.png [new file with mode: 0644]
themes/slim/slim.theme [new file with mode: 0644]

diff --git a/.gitattributes b/.gitattributes
new file mode 100644 (file)
index 0000000..f929a86
--- /dev/null
@@ -0,0 +1,2 @@
+ao-tools/libaltos/altos.dll            export-ignore
+ao-tools/libaltos/libaltos.dylib       export-ignore
index b3d2d5627233bebf65cafa6fecde3c2341de37b9..9f33ea3c437bcb562512487fdfb38d4efa5f3868 100644 (file)
@@ -1,3 +1,4 @@
+*~
 *.a
 *.adb
 *.asm
 .deps
 TAGS
 aclocal.m4
+libtool
+ltmain.sh
+src/Version
 src/ao_flight_test
 src/ao_gps_test
+src/ao_gps_test_skytraq
+src/ao_kalman.h
 ao-teledongle.h
 ao-telemetrum.h
 ao-teleterra.h
 ao-tidongle.h
 ao-tools/ao-bitbang/ao-bitbang
 ao-tools/ao-dbg/ao-dbg
+ao-tools/ao-dumplog/ao-dumplog
 ao-tools/ao-eeprom/ao-eeprom
+ao-tools/ao-list/ao-list
 ao-tools/ao-load/ao-load
+ao-tools/ao-postflight/ao-postflight
 ao-tools/ao-rawload/ao-rawload
+ao-tools/ao-send-telem/ao-send-telem
+ao-tools/ao-view/ao-view
 ao-view/Makefile
 ao-view/ao-view
 autom4te.cache
 config.*
 config.h
 config.h.in
-config.h.in~
 config.log
 config.status
+build-stamp
+configure-stamp
 configure
 depcomp
 install-sh
@@ -42,7 +54,7 @@ Makefile.in
 missing
 stamp-h1
 tags
-teledongle
-telemetrum
-teleterra
-tidongle
+doc/telemetrum.fo
+doc/telemetrum.html
+doc/telemetrum.pdf
+altosui/altos-windows.log
index 43ce393075f386c911817650f27ff0a539b20b48..bc9670f1cc46295722da9a2a86fb01bbf512cc59 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
-commit ea86b19629001059952f7572fa8564e2263dc9f6
+commit e5a55dbf265354e7c94be3e2be53c2d5c8fba056
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Sep 11 15:53:36 2012 -0700
+
+    Use ft/s for imperial speeds
+    
+    Bob Brown thinks this unit will be more useful than mph
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 73d05650eae1d3958e02e9ffde2020a2438eccbb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Sep 11 15:30:45 2012 -0700
+
+    Add Version 1.1 release notes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ae3e4cea1cd180ff18b5293a67b4520cc8292be
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Sep 11 01:00:05 2012 -0700
+
+    altosui: Imperial units for graphs too
+    
+    Just to be consistent
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 95268d681c9a6652d84db383f55a4fe8a4ac5173
+Author: Tom Marble <tmarble@info9.net>
+Date:   Tue Sep 11 12:54:31 2012 -0500
+
+    Reverted package name to 'altosui' from 'AltosUI'
+    Also added emacs backup regex (*~) to .gitignore
+
+commit 13c64f6fb5764c6a0f3520cf4e48a75d78e163db
+Author: Tom Marble <tmarble@info9.net>
+Date:   Tue Sep 11 12:44:24 2012 -0500
+
+    Add appropriate Java build deps as given from autoconf
+
+commit 8e506274a35eccacd2d4523faa08d279a201753f
+Merge: 0bc3ed5 1fc97dd
+Author: Tom Marble <tmarble@info9.net>
+Date:   Tue Sep 11 11:39:22 2012 -0500
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 0bc3ed53aa8972c7080d6335f609cd9d0df1c79d
+Author: Tom Marble <tmarble@info9.net>
+Date:   Tue Sep 11 11:37:14 2012 -0500
+
+    Use explicit build deps for altosui (avoids * wildcarding)
+
+commit 1fc97dd9875a7639533a34438c4c7c999412eb3a
+Merge: 8397d2b 4420d4a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Sep 11 10:35:04 2012 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 8397d2b041824ddf1cc0b82926f10da8aae3264f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Sep 11 10:34:00 2012 -0600
+
+    update Debian build-deps in preparation for 1.1 release
+
+commit 4420d4a9fc011ed970af506ef771dfb81580b666
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Sep 11 00:49:48 2012 -0700
+
+    Fix Latin-1 encoded copyright symbols in AltosDroid java code
+    
+    Otherwise, we get complaints when compiling these files.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 708e7937cba52982b91244cf89bfbff46d346135
+Author: Tom Marble <tmarble@info9.net>
+Date:   Mon Sep 10 16:54:27 2012 -0500
+
+    Changed package name from altosui to AltosUI
+
+commit c2ebebc4967043b16380b8ec8800862993005358
+Author: Tom Marble <tmarble@info9.net>
+Date:   Mon Sep 10 14:50:37 2012 -0500
+
+    Test commit (comment) to confirm push works
+
+commit 67da878f740a387d0092631ad672e024d26e4192
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 10 09:16:04 2012 -0700
+
+    altosui: Use units conversion functions everywhere.
+    
+    Provide a configuration option to select imperial units and use them everywhere
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 66a1e07efcac9324d33a1eca0dfb58a2724b667a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 10 09:14:03 2012 -0700
+
+    altoslib: Add imperial units conversion support
+    
+    "Redneck" mode support
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 51836cedce41d8b36eac34c69370489162aaa2b5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 14:02:57 2012 -0700
+
+    Bump version to 1.0.9.7
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e45c47c52f9cc9c43b81148e0e58fdedb3af1eb8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 14:01:21 2012 -0700
+
+    altos/telelco: Search for available firing nodes at boot time
+    
+    Query for available firing nodes, limiting device selections to those found.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 77775711d414227b3ed97859d1b21ab1c689a724
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:58:29 2012 -0700
+
+    altos/telefire: Add steady warble when the LCO arm switch is on
+    
+    And make debugging a run-time option too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f2933103be122414a9b1795b37003b7a2aa9f3d7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:57:16 2012 -0700
+
+    altos/stm: Fix basic time interval to 10ms -- was 10.1ms
+    
+    Counting from 0 to 100 takes 10.1ms, so count to 99 instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0339583996fc57a666f8d3007a0f4b1034039a73
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:56:35 2012 -0700
+
+    altos/spiradio: Label LEDs so that the radio code can use them
+    
+    Mark which should be on for TX and which for RX
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3fa71c497a5bf576974e70af06762f75734e6699
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:50:56 2012 -0700
+
+    altos: Try to get remote cmac time closer to reality
+    
+    Record the time after the packet was sent, but before the return
+    packet arrives to try and more closely approximate the time the packet
+    arrived at the other end.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 27ab744c6eec9243b7aa14161eec2fbf7003531e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:46:23 2012 -0700
+
+    altos: Clean up flight data definitions
+    
+    These just shuffle the various definitions of data macros around to
+    make the include files more sensible looking.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ced6a020d6d94b1c63837a7ab5b0091b7b8ea3c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:43:45 2012 -0700
+
+    altos: include ao_arch_funcs.h at the very end of ao.h
+    
+    Move it below the definition of the ms5607 init function
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e053da3e7cb5a4c9ebbffd245cb5d83932183b22
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:11:57 2012 -0700
+
+    altos: Allow products without MS5607
+    
+    The define for a missing MS5607 was wrong, so anything using the fancy
+    multi-sensor data code would break without an MS5607 in place.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bb5ab29b6744b382bb2f09486a7a6db7d12a3608
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:10:07 2012 -0700
+
+    ao_tools/ao-send-telem: Only start real-time on valid states
+    
+    Check state to make sure it is < ao_flight_landed to keep invalid
+    states from switching to real-time playback mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 47d87872c11a63a435fe5b703a4ce33503790d96
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:09:27 2012 -0700
+
+    altoslib: Add in a bunch of java files mising after Mike's cleanups
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e2b458a448106ba1ab207f0ea6824b56927d8547
+Merge: 9682e9e 3fe9322
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 13:03:47 2012 -0700
+
+    Merge remote-tracking branch 'mjb/altoslib_mjb'
+
+commit 9682e9e6fe730417a77b47795fbe1f06c9a51177
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 12:29:32 2012 -0700
+
+    altosui: Use helper functions to access arrays in AltosLib class
+    
+    These deal with out-of-range values correctly, instead of causing
+    exceptions that will just break stuff.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d65d921b9b2340fa23d3b55b4ae755324d392303
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 9 12:20:08 2012 -0700
+
+    altosui: Catch errors in state value when saving flight logs
+    
+    Use AltosLib.state_name() instead of directly accessing the
+    state_to_string array so that any invalid state values are caught and
+    replaced with 'invalid' instead of raising an exception.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3fe932206f40f4d6f83a4ef49e064109a7a3de92
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 18:19:43 2012 +1200
+
+    altoslib: move distinct classes to separate files.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit da053875c12b25b627e83430c3a956a994b435d5
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 18:19:07 2012 +1200
+
+    altoslib: remove obsolete class
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 218c73b5bd5bd5673dc6f259f62b39541c52d6ff
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 18:11:13 2012 +1200
+
+    altoslib: move distinct classes to separate files
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 42170d0e3a3b68a9d3db69714e043f7273a714fb
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 18:10:21 2012 +1200
+
+    altoslib: remove duplicate AltosGPSQuery class
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 82f798e7b1343a56203af2e89790f6de9ab9f98d
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 18:09:37 2012 +1200
+
+    altoslib: remove duplicate AltosIdleMonitor class
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 88667f7b7dff52eaf5e30f8f83fed0d7f767268d
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 18:07:45 2012 +1200
+
+    altoslib: rename AltosIdleRecordTM.java to AltosGPSQuery.java
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit bbbe1846346b4ba61330f535a12b7a5029877ee6
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 17:53:20 2012 +1200
+
+    altoslib: remove dead code
+    
+    (if object creation fails, an exception will be thrown - not return null)
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 02d31db3d3255568cc348a41aa37a461d63ffde2
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 17:52:27 2012 +1200
+
+    altoslib: resolve argument/variable ambiguity
+    
+    (and comment out set_flags() which had no corresponding variable anyway)
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 639f3e5e5171769429eac9e2f17a7b315fd62135
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 17:39:25 2012 +1200
+
+    altoslib: comment out unused methods
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 369c5e23ac6d65bab4b456ed86737576ac61102f
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 17:35:29 2012 +1200
+
+    altoslib: access static variables via class, not instance
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit b02c17b26e028a6f3a46781211a86a18272da4d0
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 17:34:17 2012 +1200
+
+    altoslib: comment out un-used variables
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 20bc23ddb90f8a6da1f7ea70f02cf3a038059d32
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Sep 7 17:32:07 2012 +1200
+
+    altoslib: Remove un-needed imports
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c5e98d2b226824f2012e5710ac4b1596b9f0bfb1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 6 18:07:33 2012 -0700
+
+    altos: Fix radio slave to run lights in the normal way
+    
+    These were left in a debug mode, toggling instead of flashing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 01820c3e95fe85d2bee648d41809f1a753f81020
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 1 00:15:16 2012 -0500
+
+    Revert "first work on PWM input to TeleScience, 'p' command displays timer 1"
+    
+    PWM bits not suitable for master
+    
+    This reverts commit e93c6bcc799d76d4ff425815e2601a25e6796229.
+
+commit 3ccc4a13e3f76bec864d61b0cdfd57c76c6baadb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 1 00:15:02 2012 -0500
+
+    Revert "Timer 3 working with slower clock and all 16 bits."
+    
+    PWM bits not suitable for master
+    
+    This reverts commit 49b1ff4c614d24977b33cd17b583acc87acff476.
+
+commit 8c743857525eff778d067068356dec486b9fefa2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 1 00:14:49 2012 -0500
+
+    Revert "ICP3 working"
+    
+    PWM bits not suitable for master
+    
+    This reverts commit 75d6aa6f798606f1a6c5a46542065dda81e63b2a.
+
+commit 294b1ec85a37e375a0ac70cbffc6398309d63a7f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 1 00:14:27 2012 -0500
+
+    Revert "telescience: correctly calculating rate values with higher resolution"
+    
+    PWM bits not suitable for master
+    
+    This reverts commit ada6f2dfc045e77cb9499f20cdec1b4a54ef0db1.
+
+commit ec9e1186dce079a2f2b7be8050216ddb1bc1af66
+Merge: 503eabd 6d31f8d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 31 22:24:16 2012 -0500
+
+    Merge remote-tracking branch 'mjb/freq_menu'
+
+commit 503eabd0e351ecdffda1416b7d00f8ef1d6913c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 31 22:19:40 2012 -0500
+
+    altos: Get mma655x driver limping along
+    
+    This appears to drive the chip correctly to see values from the accelerometer.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 48a3e6e073e927e456ef4e456e512f6fb8e3b9ee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 31 21:02:13 2012 -0500
+
+    altos: Fix mma665x pin assigment for mma655x
+    
+    It's on PE13-PE15, not PA5-PA7
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f9af7819de086d9179c3a4d1df7c88ab67a7d7c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 31 21:01:21 2012 -0500
+
+    altos: Add custom panic noise for self-test failures
+    
+    Make it easier to tell which component is failing self test
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6d31f8d15ef4bf75ae039dd7b1a6a615d00eb215
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Aug 31 17:53:53 2012 +1200
+
+    altoslib: add missing manufacturer parsing for AltosConfigData
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 4e3ac2b624f68dd5a8f6dd7a33eb10cd78497964
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Aug 31 17:42:22 2012 +1200
+
+    altosdroid: invert channel/freq ordering
+    
+    Matches Channel Selector in altosui
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 80bc985f79e616e5327aed4f7acc9bca71b8db54
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Aug 31 16:50:53 2012 +1200
+
+    altosdroid: also display channel numbers
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit b6a21856c68ca8cca93eb755285be1927acb91e7
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Aug 31 16:39:10 2012 +1200
+
+    ao-send-telem: fix frequency set command
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 543ecb530d6fdf188a746ac59b72544e69bad830
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Fri Aug 31 16:38:21 2012 +1200
+
+    altosdroid: complete frequency change dialog
+    
+    Also implement Service IPC to action request.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit f1a9fff865e0b226a97ad5d6eaaac64bd5e5d410
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 30 23:07:43 2012 -0500
+
+    altosdroid: Start a hacked-up frequency dialog
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6d8858ca1899c8b64f107ebb45711efbb7b8d62a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 30 16:30:04 2012 -0500
+
+    altos: another .gitignore file
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ab9d5bf13e6f1735a0463f4bcab13d65170b7015
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 30 16:28:53 2012 -0500
+
+    altos: ao_cc_spi.h isn't necessary for telelco
+    
+    The SPI radio defines are in ao_radio_spi.h
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b635cb26ba54c8f5c6a958e0ab0bc4d34d33b635
+Merge: 354c1fe a8ecf3a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 30 16:24:38 2012 -0500
+
+    Merge remote-tracking branch 'mjb/master'
+
+commit 354c1fed7f06c2c45c661e7265c2ac4bc47e2750
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 30 16:22:51 2012 -0500
+
+    altos: Add a bunch of .gitignore entries
+    
+    Clean up the git status output
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2c59954fdf204f443d9bb28c4a66a30f925ef348
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 30 16:16:52 2012 -0500
+
+    altos: Shuffle LCO functions around, add telelco first cut
+    
+    Pull LCO functions shared between LCO UI and command line into
+    ao_lco_funcs.c.
+    
+    Import bits for telelco.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 72d44d14aefcb754b871835aec8d265771357212
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 30 16:14:57 2012 -0500
+
+    altos: Disable debug printfs and fix pad ignite time for telefire
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a8ecf3aa4e88d4c76643fb541fb1d5535a454aba
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 21:08:41 2012 +1200
+
+    altosdroid: Implement voice just like altosui
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 2e6af70c87e7cc62a92b09bbbde745a31d83b5eb
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 21:08:06 2012 +1200
+
+    altosdroid: Move bluetooth check to first task
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 519887571cee2840024c5c8f4b7f0e5c352d3323
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 13:23:29 2012 +1200
+
+    altosdroid: add rssi/serial/flight, and re-work UI ordering
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 14022b002d2201fb3ca28292d976c90e2d9a15a2
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 13:23:04 2012 +1200
+
+    altosdroid: rename ambiguous TextView name
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 91606c89828e60d8cf9a5ea4ff75b951d6fc042a
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 13:21:38 2012 +1200
+
+    altosdroid: formatting/whitespace, correct ids
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 2137a112b4217d84041f749b8aa5eb8f4d330ba0
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 13:20:10 2012 +1200
+
+    altosdroid: rework lat/lon to more common format
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c32325af6605e78c1d1147d466f3ea12ce94124a
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 13:19:41 2012 +1200
+
+    altosdroid: rename azimuth/altitude to elevation/height respectively.
+    
+    (Matches altoslib, altosui, altos)
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 8ffa4f9a474026f5a6523b26919a78565e0ed74c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 13:13:20 2012 +1200
+
+    altosdroid: move units into code, to match altosui
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 583458772746317b98fced907ec780edff465888
+Merge: aea10c1 17b6ffb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 29 11:29:24 2012 -0700
+
+    Merge remote-tracking branch 'mjb/master'
+    
+    Pull in Mike's fancy new AltosDroid bits
+
+commit aea10c107dff2643677a9c8d1fc41e14f4a66049
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 29 11:25:36 2012 -0700
+
+    altos: spiradio debug serial is port 1, not port 0
+    
+    The SPI link uses port 0; it seems like the having the two try to
+    share the same wires is a bad plan.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3e7e8e21f2bb823cb6e74c73d0feddbc3a891107
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 29 11:23:13 2012 -0700
+
+    altos: SPI radio - use 1->0 for 'done' and 0->1 for 'ready'
+    
+    This changes how the SPI radio protocol uses the interrupt
+    line. Instead of a pulse indicating operation done, this now uses a 0
+    value for done and a 1 value for ready. The key distinction is that
+    the master can tell when the slave is waiting for the next command
+    instead of hoping that it got done 'soon enough'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1b6ed262460ee75fb5bb684d13d19c26c7ea750b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 29 11:22:02 2012 -0700
+
+    altos: fix ao_pad debug output for query command
+    
+    Igniter status is an array these days.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 442f1bfc89528103e2c28f768c954b956e39afc5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 29 11:21:09 2012 -0700
+
+    altos: Use updated pad protocol for lco commands
+    
+    Stop using the older single-channel protocol and switch to the new
+    multi-channel protocol
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f64e62356bcfcd6ba8a88b09251793481bcd56c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 29 11:19:17 2012 -0700
+
+    altos: Note changes to configured AES key for SPI radio convenience
+    
+    Keep a sequence number to mark when the AES key is changed so that the
+    radio code can avoid sending the key before every CMAC radio operation.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a7d2faf76a46271532102e217c2dd5515e38b72
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 29 11:16:42 2012 -0700
+
+    altos: Wait for IN dma complete on slave SPI send
+    
+    SPI send double buffered, so the DMA completes one byte too early. Use
+    the recv DMA to know when the SPI transfer is complete.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 17b6ffb6c090112367eac944494f0fa58da453c7
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 30 01:01:06 2012 +1200
+
+    altosdroid: initial attempt at a UI.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 98f65994ee547feb8cca63ff4ed0fefd3fb2d37d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 23:05:02 2012 -0700
+
+    altos: Oops. forgot ao_data.c
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3a3982ceb721910c6a4f75badebb62baa6c6568e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 22:43:10 2012 -0700
+
+    altos: Add spiradio Makefile
+    
+    git add doesn't add Makefile by default.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fc9841ee5e92318471b6bec09b7075a788ab8872
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:13:02 2012 -0700
+
+    altos: Mostly working SPI radio link
+    
+    This includes long delays to avoid overrunning the cc1111 input,
+    otherwise it works pretty well. The delays mean that we can't capture
+    the reply to a cmac command though, so more work is needed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 27a879b4069ccedf8bbe39d7dbecf45000f29d8c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:12:27 2012 -0700
+
+    altos: Include the radio_cmac debug commands in telefire
+    
+    Just temporary debugging
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eb1a9a8c3f3d3993d5986925bc4ad112c2bbc119
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:10:34 2012 -0700
+
+    altos: Explicitly erase memory in STM eeprom driver.
+    
+    This seems to make the STM32L152 happier
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3fe5a70d9ac2114ee554813b1dbb3019a3e4aff7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:08:59 2012 -0700
+
+    altos: Track protocol changes to ao_pad debug messages
+    
+    Make the debug output build again.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 18b5021e99c1eef32d2d85f619c84e89cecae7a7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:07:55 2012 -0700
+
+    altos: Stop providing debug commands in seven-segment driver
+    
+    The radio_cmac debug commands use the same letter, and this code works now...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 78cd26eec77adda23ef1b5ca2d91027f1e059868
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:07:05 2012 -0700
+
+    altos: Move radio_cmac test funcs from ao_lco_cmd.c to new file
+    
+    These are useful in the firing node for testing as well, so move them
+    to be shared.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f2d919a2147025daa332957cda6d91959e4731ab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:05:10 2012 -0700
+
+    altos: When sharing radio DMA for AES, use it for in instead of out
+    
+    We look at the out_done value, but not the in_done value; if we use
+    the radio DMA for out, we would have to use ao_radio_dma_done to check
+    for completion. This way, we can ignore that value and use the
+    existing ao_aes_dma_out_done value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 31b42b99edbb976534ac432c07e218f13d1f5f9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:03:52 2012 -0700
+
+    altos: Fix ao_delay function and move from per-chip code to ao_task.c
+    
+    ao_delay hasn't been chip-specific for a long time, and it had a bug
+    in not calling ao_clear_alarm.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ac5d053e6d766d243b7a425ae19779810c350125
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 28 18:02:25 2012 -0700
+
+    ao-stmload: Always round up load amount to 4 byte boundary
+    
+    The flashing code doesn't deal with partial writes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 68df2b1173e82d48f7857ad2e9325e6a9cbbedfd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 22:45:47 2012 -0700
+
+    altos: Enable STM SYSCFG when routing EXTI
+    
+    The EXTI routing information is in the syscfg unit, so that needs to
+    be powered up or writes to its registers will be lost.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c677f26852b70bcbb303382c306ce06664fde028
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 22:45:20 2012 -0700
+
+    altos: No need to initialize EXTI priorities at startup time
+    
+    They all get set to the correct value when enabled.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5ed88fb72c3e3ecf3333c700d838667db71cfbdc
+Merge: adbe64c 621d093
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 28 23:39:53 2012 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+    
+    Conflicts:
+       debian/control
+
+commit adbe64c5a9402b7c5075a444a12629131b663877
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 28 23:37:00 2012 -0600
+
+    libelf-dev added to build deps
+
+commit cb8f01745c9e8f258f96358b46e1caf17d6b0c9f
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 23:20:04 2012 +1200
+
+    altosdroid: update Makefile.am for source file changes.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c220369953d351f49a5f45bfb5e317859d3a9843
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 22:26:12 2012 +1200
+
+    altosdroid: Add Dumper class for testing
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 6c985c2b0433a08add3bbf55fdb30102157b4ede
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 22:10:26 2012 +1200
+
+    altosdroid: add timer to stop service
+    
+    * Stops when no UI clients, and no bluetooth connection remains
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 781bdb6c15b7dd3cc2280b08a2f47ce0f92cf53f
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 22:09:34 2012 +1200
+
+    altosdroid: do service start/bind/unbind in start/stop, not create/destroy.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit ea5fe9e95a888d623329d17d048ee360ea114ad1
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 22:08:49 2012 +1200
+
+    altosdroid: move methods around
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c6d667a6ac0decfde5bc8a180b14774e9942dd0c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 22:08:19 2012 +1200
+
+    altosdroid: tidy up old messages
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 5c7370dcd7a65c81a3c903a71167e07cfcbade53
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 22:07:39 2012 +1200
+
+    altosdroid: stop sending device name, just send config data
+    
+    * Kinda complicated, but ultimately more sensible
+    * Just send the config data as an arg to MSG_CONNECTED
+    * keep retrying connection till we actually get config data
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 5ce8c63850dbc6462d7c41ce917e0e06672ec0ab
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 22:00:14 2012 +1200
+
+    altosdroid: whitespace
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit df7b74909c3794b7b2397275e7fce9226cb99489
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 21:59:40 2012 +1200
+
+    altosdroid: override add_reply() to add android based debugging
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 9d0f187f06c411f0d82e13ee4f2faea789ed9e79
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 21:59:15 2012 +1200
+
+    altosdroid: more logical name for connection_lost() method.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 7664ecc0003151d3f05537f17914df2bf48e4275
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 21:58:47 2012 +1200
+
+    altosdroid: rework ConnectThread
+    
+    * Start input_thread after connection, otherwise it's painful to kill
+      on connect failure.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 502b24eb2c9c76e4e2bdcc79be0b71a869488b37
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 21:55:40 2012 +1200
+
+    altosdroid: fix a connection retry having a null pointer
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c7bef83fd553987f83c0bf7ff37ef941872564fe
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 21:54:05 2012 +1200
+
+    altosdroid: fix double call of stopAltosBluetooth()
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 150a726e125aa7d181c00348ddd1791fd84164e5
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 21:53:01 2012 +1200
+
+    altosdroid: Miscellaneous comments/debug/etc cleanup
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 162c640d382b9f823573578fe97584adc94cd9b6
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 17:37:45 2012 +1200
+
+    altosdroid: miscellaneous cleanup
+    
+    * Copyright info
+    * whitespace
+    * comments
+    * unused imports
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit ae03d8e87985b9f746e9e22b2394a0a5b4f39f1c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 17:36:41 2012 +1200
+
+    altosdroid: Add passing of Device Config at connect
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 03563c765d8b0ab3689c91b2b533c68e11650577
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 17:35:11 2012 +1200
+
+    altosdroid: Add new "TelemetryReader" class to handle Telemetry
+    
+    * Add MSG_TELEMETRY messages to both AltosDroid and TelemetryService
+      to handle passing of AltosState object all the way back to the UI.
+    * Remove linkedblockinglist from TelemetryService
+    * (MSG_TELEMETRY is a rename of MSG_INCOMING_TELEM in AltosDroid)
+    * commented code in case statement inside AltosDroind - won't work with
+      the objects it is currently passed.
+    * Add new "MSG_DEVCONFIG" message to AltosDroid - allows TelemetryService
+      to pass information about the connected device back to the UI.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit ed653a1e6dc8884cb171af1406fd0999ef125a4d
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Aug 28 17:26:09 2012 +1200
+
+    altosdroid: create connected() method
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 621d0930244f25165d2ac5da596dcc87e253b965
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:52:11 2012 -0700
+
+    altos: Add spiradio product
+    
+    Implements the SPI radio protocol
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 61d094f281431e9f11f806454981da8e1245fb5c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:51:53 2012 -0700
+
+    altos: sdcdb rc file for telefire
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c31d07fb35a5b4d283facf649bed3f0f9802d1fc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:49:07 2012 -0700
+
+    altos: Add SPI linked radio API
+    
+    Forward the necessary radio functions over the SPI link
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0f3483f93137f41a61f3fcbe06afcaffb1b9e17b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:47:16 2012 -0700
+
+    altos: Clean up radio APIs
+    
+    Move api to ao_radio_cmac.h include file.
+    Expose ao_radio_test as standard API.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 85fd7ab504a9fac1de90bbe7df8ea477a092c2b0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:45:56 2012 -0700
+
+    altos: Build telelco and spiradio when possible
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 467acda662de8b96e7d0df729c2e4761686b82a1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:44:35 2012 -0700
+
+    altos: Expose a signed version of the tick count
+    
+    Useful when doing time comparisons.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e7443bf350afe273e87a884915ea1e7662630cd3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:42:38 2012 -0700
+
+    altos: Specify the LCD duty cycle for stm-demo
+    
+    This is the demo for the large 7-segment displays and needs static drive
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 708d49e498bbdc59bb5af9bf4ca5fcea5689547a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:41:13 2012 -0700
+
+    altos: Test multiple quadrature devices. Export quadrature count.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8bfe8157cd9fe488d1ee961f200ffa0866322f2c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:38:44 2012 -0700
+
+    altos: Seven segment display driver
+    
+    Hex numbers and the decimal point.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 28c3923b6180e24a77aecc7162bb2852cec7d770
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:37:14 2012 -0700
+
+    altos: More SPI slave changes for cc1111 driver
+    
+    Don't enable DMA in the other direction when doing slave transfers.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a27b9b5c36cf748e415ba210c8d8ae72d8227a98
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:35:39 2012 -0700
+
+    altos: Add SPI slave get/put macros to cc1111
+    
+    Theese don't try to drive the chip select line
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 304909b7534768bfc8da62954effb37ba86806ea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:34:12 2012 -0700
+
+    altos: Provide interface for STM LCD driver.
+    
+    This provides a simple function interface for driving the LCD segments
+    in the STM chip. It also uses the update complete interrupt to block
+    LCD users during flush.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c1168f40223ca09df23215f2e2fc445a8a03aea9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 27 13:32:41 2012 -0700
+
+    altos: Rename drivers/ao_lco to drivers/ao_lco_cmd
+    
+    These contain command line functions for testing the cmac and lco
+    features. Now that there's a telelco-specific ao_lco interface file,
+    it's useful to have both for testing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fa8668931cbbc1506560222f2db7e427b514a351
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Aug 27 19:41:47 2012 +1200
+
+    altosdroid: Let a freshly connected client know what the device name is!
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 9a41508d92f95012a37bb75603e6e48a2c405204
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Aug 27 19:41:29 2012 +1200
+
+    altosdroid: Add Connected/Connect_failed messages
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit e121ec3ae634f41979717281a28af5e4a38e8f3a
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Aug 27 19:40:30 2012 +1200
+
+    altosdroid: don't need keys for Bundles anymore
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit afac4d6c82916eea67ac838cd22806bd73db00a9
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Aug 27 19:39:53 2012 +1200
+
+    altosdroid: Toast() requests don't need Bundles
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 18c380120fe37a4bdc8f295e86c6c4413d1aa037
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Aug 27 19:39:09 2012 +1200
+
+    altosdroid: really don't need to store a local copy of the device name
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 6441437d3b0e848b225a3d6c78ab00e2590c6988
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Aug 27 19:37:16 2012 +1200
+
+    altosdroid: remove complexity around message passing
+    
+    * Don't really need to use bundles
+    * TelemetryService: Use a local variable to store the bluetooth device object
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit f89e7de20374141b367205aa517a08ee203bfaf3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 26 09:53:16 2012 -0700
+
+    altos: Trigger sample complete when all data are ready
+    
+    This has each sensor mark a bit in the current data record which is
+    then sent for processing when all of the data are present.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dec1481786ad54e22634e32109b5ed6e5483938e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 26 09:52:27 2012 -0700
+
+    altos: Shrink STM stack size
+    
+    512 bytes should be enough for anybody.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d13d0fbfcb0fd6d8a1af46f6270a968d746c830e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 26 09:49:34 2012 -0700
+
+    altos: Make ao_cur_task_index track ao_cur_task in ao_yield
+    
+    This keeps the two main task references (index and pointer) in
+    agreement during task switching, avoiding an extra assignment at the
+    end of the task switching loop.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1c9baa88d6cd931c66d453674322908eb267ba4c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 26 09:47:05 2012 -0700
+
+    altos: Make 'pad' driver useful with telefire v0.1
+    
+    This reports correct status bits over the radio and to the LEDs for
+    all four channels, and also makes the firing test command control all
+    four relays.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ea4db73328e865fd658b573da256ca0004c69c61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 26 09:45:03 2012 -0700
+
+    altos: Add debug command for 74hc597 driver
+    
+    This dumps the current state of the 74hc597 chip.
+    
+    Note that this shows that the 74hc597 driver doesn't work, and that
+    the circuit used in telefire v0.1 can't work.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 89f1a685a02c7808cf03853aa9a5ee50c6baf49e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 26 09:43:34 2012 -0700
+
+    altos: Allow sharing of radio DMA with aes engine
+    
+    otherwise, telefire doesn't have enough DMA channels.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 58d2b70575f3616a056d2356a737b3be15ed3d66
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:38:33 2012 +1200
+
+    altosdroid: begin adding TextToSpeech support.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 11896d22b7bc6e34e3f6109d28f8b6a2d37e6c25
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:38:10 2012 +1200
+
+    altosdroid: whitespace cleanup
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit f8211fee3c8a5de6925eadca2679441801ac793e
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:37:58 2012 +1200
+
+    altosdroid: Send device name and connected state back to TelemetryService
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 8fdde3e74c6dce35f90f4575ce6914516fc56aba
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:37:33 2012 +1200
+
+    altosdroid: re-work connect/thread handling in AltosBluetooth
+    
+    * Much more resilient to failure
+    * handles multiple blocked threads better
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit dba71db022ae4a9f7c5fd128b90caa73aa4e99da
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:34:59 2012 +1200
+
+    altosdroid: remove old commented code
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 38827db5131a2681243649c76bfd1d7d9801f9ba
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:34:45 2012 +1200
+
+    altosdroid: add handling when restarting BT. delay start after stop.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 30d107882b62edf4e6d48923209da6ce3dabeef7
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:34:28 2012 +1200
+
+    altosdroid: set devicename and pass on to clients
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit d95b84b56c63002788939b93b6ce949d921a4892
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:30:45 2012 +1200
+
+    altosdroid: Send current state to client on connect
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit cf94a86f37284b53e89b13a7eeb871db647c61ba
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:30:00 2012 +1200
+
+    altosdroid: add missing break; (just in case!)
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 24fe48f66f94c99b8197a612afe6f98e980e9796
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:29:25 2012 +1200
+
+    altosdroid: debugging statements
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 2ef1723091b0c491ef445474844376185ca4102b
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:28:59 2012 +1200
+
+    altosdroid: forgot change to IncomingHandler constructor call. oops!
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit d40f96fcc961cfbf6af67fc84591d2660d065ca0
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:28:26 2012 +1200
+
+    altosdroid: Strings and Layout changes
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 3d6fc5fe462531e05ca4b9be1a421490e067a28b
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:26:58 2012 +1200
+
+    altosdroid: lots of debugging statements
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 31bffa435cec2098c7ab5c42c829ba6e1578b5d2
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:24:13 2012 +1200
+
+    altosdroid: need sendMessageToClients() for setState().. oops!
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit a9ec3c96288b7ea4e40586321a0a98edf0c8fee5
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:23:33 2012 +1200
+
+    altosdroid: Need access to handler inside AltosBluetooth
+    
+    * Also move add_monitor() call
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit d7173e814c49826f39bba1ff6b024819c555860c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:20:01 2012 +1200
+
+    altosdroid: reflect change in message name
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 215d78f06093bd8a8b08a85cae0f1f34aee2a6ec
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:19:06 2012 +1200
+
+    altosdroid: begin adding state support
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit fe6680dd3b4c31b3d4edc3f06a142f02bcb879df
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:16:49 2012 +1200
+
+    altosdroid: init device variable... oops!
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 2c5513c51b187ad26a59b193b401f38c35141d27
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:16:04 2012 +1200
+
+    altosdroid: Rename Connect message, add connected message
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit a6373e84393312ed0fbf22285c704819c2011588
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:14:09 2012 +1200
+
+    altosdroid: init telem blocking list.. oops!
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 21359f600354e8ee840e839e61ef97d30f3586fc
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:13:33 2012 +1200
+
+    altosdroid: disable NotificationManager stuff for now
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 54baecc208a40606e3242b2cbd5e66567053646f
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 23:12:48 2012 +1200
+
+    altosdroid: Convert handlers to use weakreferences
+    
+    * Also renamed bluetooth start/stop methods
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit bf7def1a7b93867dfe16fe6499ee028747634c41
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:28:58 2012 +1200
+
+    altosdroid: Remove Binder import from TelemetryService
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 5f4c47389a3d0d10d659a2e00fc74a150b5fed88
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:28:36 2012 +1200
+
+    altosdroid: Add State constants for future usage
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 6ffcc82d8d18d3f05d4f5881e50dda298b43c114
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:28:09 2012 +1200
+
+    altosdroid: begin adding IPC to TelemetryService
+    
+    * And add imports for LinkedBlockingQueue... oops!
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit a33333b97e810f50db36f345aab71a3200feccc3
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:24:19 2012 +1200
+
+    altosdroid: remove old Binder from TelemetryService
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit b69796991c1da6baf245349fcc4392668b9b5570
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:23:17 2012 +1200
+
+    altosdroid: begin adding IPC to main thread
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit bcd53483ccf4bbb2f163a011faae6d19a7bbed0d
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:22:23 2012 +1200
+
+    altosdroid: Add TBT initialisation to AltosBluetooth
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit ef29a197ce3318404f37e8a0b24d235e8b024a1f
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:21:57 2012 +1200
+
+    altosdroid: Add debugging statements to AltosBluetooth
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit fb8cd14cca61ca59b95c23e71505607b4509d4ed
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:21:36 2012 +1200
+
+    altosdroid: Add input thread for reading from TBT
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 5ce132b3366cd120499fcbe22b5fbe96d21b8584
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:20:42 2012 +1200
+
+    altosdroid: Move constructor for AltosBluetooth
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit b59e7d0b201290f2cb0fd494ef28c1402e11ba3b
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:18:44 2012 +1200
+
+    altosdroid: clean up stub functions in AltosBluetooth.java
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit d184638be79dafd6fb43df21040eb52402f54ea5
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:11:12 2012 +1200
+
+    altosdroid: AltosBluetooth.java
+    
+    * clean up variables/comments
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit bad155538c4630c62ade80afd20830aad37c287e
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:10:15 2012 +1200
+
+    altosdroid: AltosBluetooth.java
+    
+    * Clean up imports
+    * Convert from reflection to using Well Known UUID for SPP.
+    * clean up local variables
+    * Add debug conditionals to logging
+    * remove references to socket type
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c9689a3ef65ea9da5a7009834add789737ffb6a9
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:03:54 2012 +1200
+
+    altosdroid: Clean up imports in TelemetryService
+    
+    * Begin adding AltosLib usage
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit ffdfc08c317f503e30604d058749b24c3ca7bafa
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 15:00:23 2012 +1200
+
+    altosdroid: Add service start/bind/unbind to AltosDroid
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 95a34caa8343997bcf7d8969ee8ae3124efcb573
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:57:04 2012 +1200
+
+    altosdroid: Remove AltosLib import from main thread
+
+commit cfe93315fc0e4b01a95b8e59f24aca96b5a66daf
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:55:19 2012 +1200
+
+    altosdroid: whitespace
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 3f3da6626ef41b2cab116d6299d2a89cbf7718a9
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:54:54 2012 +1200
+
+    altosdroid: Re-locate TextView initialisation
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 917f519a4e876087590a3a260fbbccf4c0ac3e31
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:54:06 2012 +1200
+
+    altosdroid: remove UI components/imports no longer used
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 80bf63702175322053f2b38c4fff56b653ab7c70
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:52:30 2012 +1200
+
+    altosdroid: excise BluetoothChatService example code
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 372840b4ebfd3da3cd713b6bc6a8ffc8cd6b6b8c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:48:48 2012 +1200
+
+    altosdroid: remove TelemetryService activities
+    
+    service start/stop will be handled by AltosDroid/itself now.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 24503eb330bf887f5c76afe2aaa9c9f2ce177460
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:44:09 2012 +1200
+
+    altosdroid: whitespace (spaces to tabs) (part2)
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 7aab6e6b6e361455a7515fe6db7b0e9a6e4c786c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 14:29:36 2012 +1200
+
+    altosdroid: whitespace (spaces to tabs) (part1)
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 0f3597389977f86a8c1bdff1b7f46107c43ef306
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Aug 26 12:41:24 2012 +1200
+
+    altosdroid: Update copyrights
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 7ecb429fe4682faf209452b1738ff3c8096fc5d5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:46:55 2012 -0700
+
+    altos: Configure telefire SPI and LEDs correctly
+    
+    This places the telefire SPI bus on USART1 option 2 and
+    marks the various LED functions with symbolic names.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 310c1d0bf83448eb12e5d64ac09a4279c25fd258
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:44:35 2012 -0700
+
+    altos: telelaunch doesn't need ao_radio_cmac_init
+    
+    This function was used to initialize the LCO functions, which were
+    moved to ao_lco.c a few commits back, so ao_radio_cmac_init doesn't
+    even exist anymore.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6581fa1641882198c870c2f7b1340794b9d47f29
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:42:32 2012 -0700
+
+    altos: Get ao_pad.c working on telefire v0.1
+    
+    Monitor all four channels, allow any channel to be fired.
+    Turn on power LED at startup time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 83e0d4352041b3964ea7a133b0f67da9ebaa1c77
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:40:27 2012 -0700
+
+    altos: Split out LCO functions from ao_radio_cmac.c to ao_lco.c
+    
+    These functions are not used in the telelaunch or telefire nodes, so
+    don't force them to be included.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c64999c39d6d9242f98bdc9312436c3333115bfd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:39:46 2012 -0700
+
+    altos: Have radio_cmac turn on LEDs as appropriate
+    
+    Use AO_LED_RX and AO_LED_TX defines if present to control LEDs during
+    radio operations.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a8e9906513227600599da12b268ff5f807ae98c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:34:55 2012 -0700
+
+    altos: Add 'ao_led_set_mask' in the PCA9922 driver
+    
+    This lets a subset of the LEDs be controlled independent of other LED settings.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 50b58d703b8b48005a3ca9ad4e3603d6f7ac1430
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:33:38 2012 -0700
+
+    altos: Make cc1111 SPI pins configurable
+    
+    Allow either USART in any configuration. Still only supports one SPI
+    bus though.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7a0ed0ff4192060854d69e640de2c30105eb2f62
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 24 00:27:13 2012 -0700
+
+    altos: Disable telefire 'pad' listener when testing radio
+    
+    Ensures that the radio testing won't block waiting for a firing mode
+    packet to arrive.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 659c0cd3ee4b9581c12ac2cd1b4162bf07a921ce
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 19 10:02:16 2012 -0700
+
+    altosui: Check for JRE 1.7 in Windows installer
+    
+    altosui runs fine with version 1.7 (on Linux at least), so allow that
+    version to satisfy the java check instead of requiring the user to
+    down-grade to 1.6
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ab1279cc6a683595631d7ac8bed7b36e0c8a691c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 18 22:51:47 2012 -0700
+
+    Here's the button driver and event queue logic
+    
+    These were neglected in the commit which was supposed to include them
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93db8febda03d9a02c5c1ccdc6ad44eaf00a433b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 18 21:26:34 2012 -0700
+
+    altos: Add MMA655X driver
+    
+    Just debug code at this point, will complete on real hardware
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8457ca81adc9d62a7ffe56a7c0c36f2fcadaa682
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 18 21:25:34 2012 -0700
+
+    altos/cc1111: Document SPI bus pin options
+    
+    Just a comment in the source code about which pins each option selects.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f46d94ca5c969c88dd307d1b684d6fafa2157020
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 18 21:24:16 2012 -0700
+
+    altos: Use split SPI bus for MS5607 sensor
+    
+    Leave CS low while releasing the SPI bus when waiting for conversion
+    complete -- other SPI bus users will use another set of pins.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a1a48aa9ee0bf7fa6720b34c0f544485caea7cac
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 18 21:21:58 2012 -0700
+
+    altos: Allow STM SPI bus on multiple pin sets
+    
+    This allows multiple STM pin groups to be used for each SPI
+    bus. Useful for the MS5607 sensor which signals conversion complete on
+    the MISO line.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 00abbbb79de67dc95176fe48b23ce3e8614e8d3a
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Wed Aug 8 07:07:49 2012 +1200
+
+    ao-send-telem: make --realtime work
+    
+    (add "break;" to the case statement for options)
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 9456332fc16269270a2e9b7ef0b54523800cfe27
+Merge: bd02349 4d4ad34
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Wed Aug 8 06:49:15 2012 +1200
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit 4d4ad34aec0c75c66162b992f1e52947e4685730
+Merge: c7f2285 8e4ebd1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 7 11:48:20 2012 -0700
+
+    Merge remote-tracking branch 'mjb/master'
+
+commit bd02349111ae0f39b320e6a10a330051ddc39fdf
+Merge: 8e4ebd1 c7f2285
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Wed Aug 8 06:46:56 2012 +1200
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit 8e4ebd1f6eb928b5cb7bcda4ed88851aa9e61bdf
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Wed Aug 8 06:45:03 2012 +1200
+
+    Add ao-send-telem to .gitignore
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c7f228503870c44dfd278ede8b0980dbac73d3c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 7 11:32:16 2012 -0700
+
+    altos: Fix .sdcdbrc file for telebt-v0.1
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c58f3d1a373b20fac3f51037008bcc40955f1348
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 7 11:30:47 2012 -0700
+
+    altos: Oops. Serial 1 stdin was busted by typo
+    
+    A typo in the symbol used to enable stdin wakeups from serial1 caused
+    the input to pend until some other wakeup occurred.
+    
+    This also makes the serial1 hw flow control pin selects in config 2
+    work right, although those aren't used by any current product
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 46f87373bc8c28442273ee4f8da3a352223150f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 6 22:53:52 2012 -0700
+
+    altos: Add button driver and event queue
+    
+    With this, a single task can wait for any button or quadrature input
+    device.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 11046bc89b3ce6386f1005fc8476b08f54d6f5fb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 6 21:54:58 2012 -0700
+
+    altos: Support multiple quadrature encoders.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5f7e61c749b02ed16e368502062e39b0471e9257
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 6 20:20:32 2012 -0700
+
+    altos: Fix up quadrature driver
+    
+    Mostly works now, should work reliably with a bit of input filtering.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ab379493dbe9923db8e458d2f4e0344df17d331c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 6 19:35:02 2012 -0700
+
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+    
+    altos: Test quadrature driver
+
+commit b0b52ca73bc836336ecc70247a9ed1dd633920d9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 6 19:34:34 2012 -0700
+
+    altos: Add quadrature driver
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6171892fa32e8a662a494ec6ba28a82fddc68589
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 6 19:33:44 2012 -0700
+
+    altos: Add ao_gpio_get and ao_exti_set_mode
+    
+    Needed to support general GPIO interrupts
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit de701d5a234cd21930cf92c9cabebb0e230da9b5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 5 14:58:40 2012 -0700
+
+    altos: Build ao_kalman.h from cc1111 subdirs as needed
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fc9aed1ef3485d259722c9b89e19969e0afe257c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Feb 26 22:30:09 2012 +1300
+
+    Adjust service to be persistent.
+    
+    * Run as a foreground service -> high priority
+    * Notification set to display as "Ongoing".
+    
+    Changed logo to be in colour at keithp's request. Greyscale logos still present as *_g.png
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit b80d1f4e8a2df3ace64468a38a815a4f982aa179
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Jun 10 20:04:24 2012 -0600
+
+    move from Debian packaged sdcc to new cc1111 package forked from 2.9.0-5
+
+commit 0f82021186565fda10df7893b95deae4a1f32778
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 5 12:17:25 2012 -0700
+
+    src/cc1111: Enable SPI slave mode
+    
+    This is untested...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 39594fdb3e30a1a25dd894c217e3d9d773bab972
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 5 12:16:25 2012 -0700
+
+    src/cc1111: Allow serial0 without serial1
+    
+    The ao_serial_speed structure is needed by serial0 too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2610b316eb939c1532061646b05207fcd54d984f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 5 12:15:36 2012 -0700
+
+    src/cc1111: Add ao_gpio_set macro
+    
+    Allows general GPIO-using code to run on cc1111
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 82a37d70e3cacf792c1aa18f8c0d2a19d6f321ed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 4 22:58:48 2012 -0700
+
+    altosui: Move 'implements Runnable' from AltosSerial to AltosLink
+    
+    AltosLink is the class providing the 'run' method, after all...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit af34baf5dc587bee4cffa699ef383f85dde8c7cd
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 2 23:38:26 2012 +1200
+
+    Reduce size of textview.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 359d7353fd7b7d4d537db04c5e89724502333ff8
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 2 22:09:24 2012 +1200
+
+    AltosDroid: Begin re-working Bluetooth code
+    
+    * Move to using explicit 'magic' UUID, rather than java reflection
+    * Re-work UI to make it more useful for testing
+    * Use Insecure RFCOMM only, and remove code that differentiates.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c5304ac976dd44344a0b70ae3622e1f2d112a147
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 2 21:20:23 2012 +1200
+
+    Fix min/target SDK versions
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 7481d06bebc2dc1473f451971d8b744c9da4e726
+Merge: 599e28b c56dead
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 2 21:18:15 2012 +1200
+
+    Merge branch 'master' of ssh://mjb@git.ethernal.org/~/git/altos
+
+commit 599e28b2242c79bdd0960ef16e580e51a2fa3795
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Aug 2 21:16:54 2012 +1200
+
+    Re-work external lib support for newer Android SDK
+    
+    * Remove older *.properties methods of locating lib dir
+    * clean up Eclipse classpath file
+    * adjust Makefile.am to link AltosLib.jar into libs/
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit c56dead72f65e7468017656347dba531ab2ca480
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Jul 31 20:05:35 2012 +1200
+
+    Ignore autogenerated file: altosui/Info.plist
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit fb79f8fb358f8df25674336cd558fc3998cb7d9e
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Tue Jul 31 20:04:34 2012 +1200
+
+    Don't build ao-stmload if stlink is not available.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 2e7e304e67bc1e094282c8668fa8cccf09f9c9b4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 29 19:48:08 2012 -0700
+
+    altos: Add driver for STM internal flash
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 843fcab46d633e5bb6959286adeb68e41a4c30a3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 29 19:44:56 2012 -0700
+
+    altos: Add telefire-v0.1
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e4d244eefa4c779cd9c8a91389bf998c54705b72
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 29 19:42:53 2012 -0700
+
+    altos: Add software AES implementation
+    
+    This is untested
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 21e39811bd234c6f66ab7644864fcc1b8c316998
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 29 19:36:50 2012 -0700
+
+    altos/cc1111: Fix serial 0 option 2 pins definitions
+    
+    tx/rx are 4/5, rts/cts are 2/3
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 743dca54012758d3ae54312d542b34afa88495cd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 29 19:35:15 2012 -0700
+
+    altosui: Remove duplicate values from info table. Add altitude
+    
+    No need to have state/call/serial/flight data, those are all in the
+    header. Having altitude makes Monitor Idle slightly more useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0bf21399d3d47d58410df4c6ce89fc20fcd42c89
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 29 19:34:00 2012 -0700
+
+    altosui: Handle Monitor Idle errors better
+    
+    Deal with missing data by checking for MISSING in more places.
+    Handle serial communication failures during send by reporting back
+    from libaltos.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 59588ba34159b27c02e1a886b46497ecfa0cf4d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 29 16:22:23 2012 -0700
+
+    Add ability to re-play telemetry through TeleDongle
+    
+    This adds a new command to TeleDongle to send arbitrary data, and then
+    creates a new tool, 'ao-send-telem' that replays existing telemetry
+    files through TeleDongle.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 52e920bc7a98edf5c6f2ad0bd59d581011dcd5c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 22 15:18:27 2012 -0700
+
+    altos: Move ao_radio_cmac.c to core (it doesn't depend on hardware)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 233ab58df8ac8e1fdeab8d4c2f6c8c9d3f6e7be1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 22 11:53:44 2012 -0700
+
+    altosui: Move AltosIgnite.java to altoslib
+    
+    To be shared with altosdroid eventually
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 38f66a31174dd367e39d717c527f555add60a9d4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jul 20 12:04:17 2012 -0700
+
+    tools: Use pkgconfig to find stlink for ao-stmload
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e033a72d3f420e18ed24354c7dfc7e1317a03fb6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jul 20 11:08:22 2012 -0700
+
+    Add stlink pinout
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 15ebd9c75aa57572040e3b1ee41e6f3eb8cf92ee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 19 09:46:20 2012 -0700
+
+    altosui: Delay starting KML output for flight and GPS coords
+    
+    Don't start outputing KML data until the telem record containing
+    flight number and GPS coordinates are present.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ada6f2dfc045e77cb9499f20cdec1b4a54ef0db1
+Author: Robert Garbee <robert@gag.com>
+Date:   Wed Jul 18 18:41:00 2012 -0600
+
+    telescience: correctly calculating rate values with higher resolution
+
+commit e2b472bbb2418fc13be42dbc7c52beb88479c46d
+Merge: 75d6aa6 b242f27
+Author: Robert Garbee <robert@gag.com>
+Date:   Wed Jul 18 14:25:27 2012 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 75d6aa6f798606f1a6c5a46542065dda81e63b2a
+Author: Robert Garbee <robert@gag.com>
+Date:   Wed Jul 18 14:24:05 2012 -0600
+
+    ICP3 working
+
+commit 49b1ff4c614d24977b33cd17b583acc87acff476
+Author: Robert Garbee <robert@gag.com>
+Date:   Wed Jul 18 13:41:27 2012 -0600
+
+    Timer 3 working with slower clock and all 16 bits.
+
+commit b242f2756a8d9419a9bdba890b9e6b73560bdc19
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 18 00:03:54 2012 -0700
+
+    altosdroid: Start adding an AltosLink subclass for android
+    
+    Will talks over bluetooth while providing an AltosLink APi
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f164e48cbeff521d45737794e2046a08322951d6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 18 00:01:51 2012 -0700
+
+    altosui: Make scan UI handle incremental telem data
+    
+    The new telem format doesn't send everything in each telem packet, so
+    we need to handle updating information incrementally in the scan
+    results. This involved clearing old scan data when switching
+    frequencies and then updating existing entries with new data as it arrives.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a698cd68968fc0be5f96b1729cdea2f65d2ccbf6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 23:59:13 2012 -0700
+
+    altos: Toggling telemetry monitoring would replay the telem queue
+    
+    Using the 'm' command to turn telem off and back on would end up going
+    around the whole telemetry queue replaying everything there as the
+    wait loop would exit when disabling monitoring even if the ring was empty.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9fd5e3c28fb0fd6da8641e7dd18b9912866d1b75
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 21:06:07 2012 -0700
+
+    altos: With debug cables disconnected, CC1120 seems happy at +16dBm
+    
+    Let's see how it goes...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 700818c8ff0518e79bff2f0e80b2cc3cb3b48bf0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 21:04:58 2012 -0700
+
+    altosui: Accept variations in spacing for igniter status reply
+    
+    Megametrum uses different white space; just deal with it here.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f6921c9040b1f1fc4408d163532b0695a3611195
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 21:04:23 2012 -0700
+
+    altoslib: Move idle monitor managing code to altoslib
+    
+    More stuff to be shared (potentially)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b4be63627e146c7c868b5b3468d34880a561cfba
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 20:44:55 2012 -0700
+
+    altos: Megametrum ADC values are 12 bits, discard low 4 for telemetry
+    
+    We've only got space for 8 bits for the ADC values for pyro channels,
+    discard the low 4 bits instead of the low 8 bits.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97c1cfee929a35dd1596dc02ce519b80132f3c5c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 20:43:31 2012 -0700
+
+    altoslib: Don't use MISSING values in max computations
+    
+    Early telemetry state may be missing critical data, don't use MISSING
+    values in computing max ranges.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 99b7eaf8d1b312443b842d078fb8464032b3a39b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 20:42:42 2012 -0700
+
+    altoslib: Fix MM conversion functions
+    
+    Pressure is already reported in Pa, no need to convert that.
+    Voltage divider computations were backwards.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e93c6bcc799d76d4ff425815e2601a25e6796229
+Author: Robert Garbee <robert@gag.com>
+Date:   Tue Jul 17 13:23:48 2012 -0600
+
+    first work on PWM input to TeleScience, 'p' command displays timer 1
+
+commit a5d873d47b3b16ca32559b4de668bf07b25eddb0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 01:24:52 2012 -0700
+
+    altos: Place STM config values at fixed addresses for re-use
+    
+    Just like cc1111, stick the serial number and radio calibration values
+    at known fixed addresses so that when re-flashing the board, we can go
+    find the existing values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 20877ae9de8bb5d3a29e2a96024e53afbd396f55
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 17 01:18:45 2012 -0700
+
+    Add ao-stmload tool
+    
+    This loads an ELF image through the STlink programming interface using
+    the stlink utility library
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eda636c5f309b85282b4142118ee65673d28d137
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:36:20 2012 -0700
+
+    altosui: Skip unknown data when parsing ADC for idle monitoring
+    
+    This resolves an infinite loop when talking to megametrum.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 52196975c447851f14619213c1de5101d334eebc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:35:11 2012 -0700
+
+    altosui: Move serial datastream parser to altoslib
+    
+    instead of having it in altosui
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0edb5616a70fd480317acc99ec3c28c662aa8556
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:34:01 2012 -0700
+
+    altos: megametrum depends on ao_companion.h
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5860f75677ee20fcf35ab89a6b62f3e14a1c32f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:32:26 2012 -0700
+
+    altos: Enable pyro channel control in telepyro
+    
+    This should make the board actually work now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3ce645a79b54e22d7835c6e390a22a5ad501a339
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:30:29 2012 -0700
+
+    altos: Add manual firing command for extra pyro channels
+    
+    In parallel with the existing igniter commands, this tests the
+    programmable pyro channels
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6581eefbdbd8d3e94f615bdf11652a000d131c8e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:27:58 2012 -0700
+
+    altos: Use 'HAS_ORIENT' to enable orientation selection in pyro code
+    
+    Don't expose orientation options when no orientation data is available
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1887ca3d7d4a0259686f8c1e68d1e47c47b4ab84
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:25:47 2012 -0700
+
+    altos: Pass flight dynamics to companion boards
+    
+    Necessary for TelePyro
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 90507d4f7ef77b0870a032b1d9809898c2924721
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:24:21 2012 -0700
+
+    altos: Make ao_tick_count visible
+    
+    Necessary to fetch it when interrupts are already disabled as we can't
+    call ao_time then.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 184e2ff4790974733df57facdeeb96bfe692ab54
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:21:21 2012 -0700
+
+    altos: Remove some debug code from avr ADC driver
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e7ca3a7849b3cbbb38143d35fe86972290bd4e61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 15:18:12 2012 -0700
+
+    altos: Make sure telepyro ao_product.h is built before .o files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4de789331098abc24abcb9390a35aef889a41784
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 14:47:53 2012 -0700
+
+    altos: Make storage addresses datatype configurable
+    
+    No sense using 32 bits for tiny hardware
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 84d5e00d417af4924594908d19346bb965089cdd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 14:44:40 2012 -0700
+
+    src: Add driver for AVR internal eeprom. Use for telepyro config.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2a23030031c31267fc4f14accd9220f285c03b61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 14:40:49 2012 -0700
+
+    altos: No space for pyro help on TP v0.1
+    
+    AVR doesn't have enough ram to hold the help messages, and we can't
+    use them in flash.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 366217e86a4c353012b5102322ee6927f7b27a21
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 14:39:05 2012 -0700
+
+    altos: Remove unused 'func' from ao_config_set
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 81b7b58c9df01847fa47747deeff8c3c91304ad1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 14:37:58 2012 -0700
+
+    altos/avr: Remove debugging command from USB driver
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 42a51becf4b76f23dbd4f5f80f8879ce696aa543
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 16 14:36:51 2012 -0700
+
+    altos/avr: Remove debugging printf in avr stdio startup
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0cfd22baa6af44e053428c30c1a95cf5551b68af
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 14 02:44:17 2012 -0700
+
+    src: Add explicit 'pin' argument to ao_enable_output
+    
+    This lets the cc1111 use the atomic bit operation instead of a mask,
+    which is immune to interrupt issues as well as being a shorter code sequence.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 37032e4b0cbac4c823e3dd18e60ad8900e9ceff1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 14 01:29:50 2012 -0700
+
+    altos/megametrum: Support the four additional pyro channels
+    
+    These use the new pyro code to allow for some flexibility in programming.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 82b50fc1b7f2c6af7264fbad2c35508abc15e81e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 14 01:28:55 2012 -0700
+
+    altos/stm: Expose ao_gpio_set which sets a specific GPIO pin
+    
+    Will need versions for other architectures
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 726e0f2c547b6bae1f1e640e2c1155c0b9631a9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 14 01:28:31 2012 -0700
+
+    stm: running out of memory in MM -- reduce stack to 668 bytes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d90587535676f9492f0fde6b974353158104ef88
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 14 01:26:38 2012 -0700
+
+    altos: Add arbitrary pyro channel support
+    
+    Programmed by specifying a conjunction of flight conditions that
+    trigger the igniter to fire.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 09633cac697e37d770b2b666ab20cab30628484f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 14 01:24:14 2012 -0700
+
+    altos/stm: Force STM stack to start at the top of RAM
+    
+    Using a fixed size means crashing if there's not enough space for
+    that, or wasting memory if there's too much.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b5f6d4e5251a825395c93916afa3af659c678498
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 19:15:32 2012 -0700
+
+    altosui: Abstract remote connection timeout stuff
+    
+    This moves some of the logic for managing when to present the 'cancel'
+    dialog for remote operations to altoslib.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f078a591cf2fafe89bb1bb883f49d80750129d44
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 14:28:53 2012 -0700
+
+    altosui: Remove a bunch of debugging printfs
+    
+    These aren't useful at this point.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 846a6298e4a8bfbe87bb24d7b0802c0bf6f233be
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 13:53:30 2012 -0700
+
+    Report RSSI values in monitor idle UI (trac #44)
+    
+    This adds a new 's' command to TeleDongle to report RSSI value from last
+    received packet, and then has AltosUI request that value when closing
+    the remote link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cf44ea354c2d1780cee343132c6058e11e9eefa3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 13:52:32 2012 -0700
+
+    altos: Fix gcc compiler warnings in GPS code
+    
+    unused variables and mis-matches in printf format codes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fe70611c3c7d4b8cce3b5292f0ec549f3191bf86
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 13:50:56 2012 -0700
+
+    altos: Create macros to convert from/to radio RSSI representation
+    
+    AO_RSSI_FROM_RADIO and AO_RADIO_FROM_RSSI.
+    
+    Removes a bunch of open-coded versions of the same function.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 18431c88c8a6cb267922b97192e8b7ddb88d0e7e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 13:49:26 2012 -0700
+
+    altos: Have 'make clean' remove all programs, even old ones
+    
+    This makes sure that changing version numbers doesn't leave old
+    compiled output lying around.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 726f47c8a07f060aed930e1d102a1e8b5a5c7aed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 13:47:25 2012 -0700
+
+    altos: remove optimization for 'help' that confuses cc1111 compiler
+    
+    The cc1111 compiler gets this very wrong and prints piles of garbage
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7be98836e69a222b2f9f4baacddcf12d168e2207
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 13:40:54 2012 -0700
+
+    Add megametrum outline to doc dir
+    
+    And install it alongside telemetrum-outline
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cc5d106f014f714a9a2d5f595a2de0da8f7da80a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 13:40:07 2012 -0700
+
+    altos: Ignore megametrum built files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 675ccd41e3b668cd4e1d2dd282dd317a00d00151
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 11 00:35:21 2012 -0700
+
+    Get AltOS version numbers into Mac 'about' dialog
+    
+    Generate Info.plist from Info.plist.in to correctly set the VERSION
+    information. This also changes some strings around to make them look better
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 46407f7f3e4d2b6e74b3a2e90b38736a792cfc54
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 22:41:59 2012 -0700
+
+    altos: Force 434.550Mhz by connecting  debug gnd and clk (trac #41)
+    
+    Check for pin P2_2 low at startup and set the frequency to
+    434.550MHz. This value won't get written to flash, so rebooting again
+    will restore the configured frequency.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aa305da7e5dc182c99c09e422c053d85ed48d5d5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 22:09:44 2012 -0700
+
+    altos: Continuously update ground state while on pad (trac #42)
+    
+    Average data for 5 seconds, wait 5 seconds and if still in pad mode,
+    replace the existing data with the new data. This should avoid
+    averaging in boost data while still keeping things reasonably current.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0b92164143aaf0d2aa3d5d742484391c16545289
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 22:08:28 2012 -0700
+
+    altos: Set HAS_FLIGHT_DEBUG=1 to include the flight debugging commands
+    
+    This leaves USB enabled, and adds the 'F' command to dump
+    internal flight state.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fb60d87b02c0fc83a0b4268212f0b6b740c984e3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 20:35:19 2012 -0700
+
+    altos: Signal continuity over radio in pad mode (trac #40)
+    
+    This is especially useful for telemini which has no beeper, allowing
+    you to hear the continuity signal while at the pad over the air.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bca72b782a2909ecedef15ad589292647221ca56
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 20:34:02 2012 -0700
+
+    altos: Add 'void' parameter to ao_fec_decode callback parameter
+    
+    This keeps the compiler from whinging about the lack of a prototype.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aafa8859ecb27383f697b98f6991643b44f4721a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 20:32:31 2012 -0700
+
+    altos: Save some memory.
+    
+    A few minor space savings in ao_cmd.c and ao_config.c.  Don't build
+    unused conversion functions ao_altitude_to_pres and ao_temp_to_dC
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a60c5a728530e4659a6750d8d5b87000735d4531
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 15:15:00 2012 -0700
+
+    altos: Improve ao_flight_test a bit
+    
+    Add -i flag to include flight description
+    Have run-tests know how many failures to expect
+    Add run_baro to plot a single flight using the baro-only mode
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b89d37d357273b97050c00d7fe12022e32799fa8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 15:13:55 2012 -0700
+
+    altos: add some (unused) test code for different soft-decision sizes
+    
+    This lets us experiment with hard-decision and other possible
+    soft_decision bit depths.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6694cedd560a7ea9520ef11472c2770b489187c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 15:13:18 2012 -0700
+
+    altos: Eliminate compiler warnings when building ao_flight_test
+    
+    We turn on a pile of warnings for that.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1ae3f467a1d7be2fc3b1a45ba12568a3a25a0099
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 15:11:36 2012 -0700
+
+    altos: Rename *_mm.c back to *.c
+    
+    Was just a temporary hack to keep cc1111 products building during MM development.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 702ca87983594880d7926d2317d63802af82746e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 10 15:07:34 2012 -0700
+
+    altos: remove stale ao_flight.c and ao_sample.c
+    
+    The mega-metrum versions are now the official versions
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 31b05454ec8d90b89fa3039563ff0e86ae80b2a0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 3 21:02:20 2012 -0700
+
+    altos: Move profiling settings to Makefile
+    
+    Instead of splitting the changes across Makefile and ao_pins.h, put
+    them both in Makefile to simplify enabling profiling.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 939ad8bfd640ed55116703a58f87af06e75ef87e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 3 21:01:01 2012 -0700
+
+    altos: Crank up the gaussian noise in the FEC test
+    
+    This lets us check to make sure our receive performance isn't
+    degrading at all, instead of just making sure we can receive
+    perfect packets well.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e8ab00cc45e48230e3b2018ce959114d3fedd228
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 3 20:59:35 2012 -0700
+
+    altos: Viterbi performance improvments. Down to 5.7ms for command decode
+    
+    Stealing more of Phil's good ideas, decoding a command mode packet has
+    been reduced from 9ms to 5.7ms. Lots more comments to help future-me
+    figure out how this code works.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ea957f9e6144f8411ac84ee2905700f55f5a6e8a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 3 00:29:43 2012 -0700
+
+    altosui: Fix flight data download for TM. Look for MM flights when graphing
+    
+    A couple of minor fixes, the first to not force the log format so that
+    TM/Tm data will be downloaded correctly and the second to expand the
+    set of files to include '.mega' files when plotting data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 933fc7e4c2f617e49e1cfdf45b83695290b51456
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 2 23:44:10 2012 -0700
+
+    altos: Do not block igniters on the radio mutex.
+    
+    Blocking igniters on the radio mutex fails when doing igniter testing
+    over the RF link; the packet receiver task will never release the
+    mutex and the CPU at the same time, causing the system to lock up.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 50b343d389039eae082e82b8ac0b76ae3e2b3ad4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Jul 1 23:24:20 2012 -0600
+
+    extend cross-compiler availability logic to all target CPUs, not just ARM
+
+commit f0ec8416f2e308e40e1b9f34a7c2549989fee5fb
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Jun 30 13:00:33 2012 -0600
+
+    conditionalize build of ARM binaries on presence of arm-none-eabi-gcc in PATH
+
+commit edbc5d27c8c2936b59ff5289276d9198b501ebc8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 28 17:43:36 2012 -0700
+
+    altos: Declare cc1111 accel_ref as unsigned so the math works
+    
+    If accel_ref is signed, then the careful shifting and dividing dance
+    necessary to correct for changes in the relationship between the 5V
+    and 3.3V supplies always generates zero.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ccf64117662fc800a07b3a25e52255b80f8b5eaf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 28 16:42:59 2012 -0700
+
+    Update version to 1.0.9.6
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca1bc20735a170a77066f5f37e0ad728899a3989
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 28 16:05:56 2012 -0700
+
+    altos: Disable MS5607 interrupt in the handler
+    
+    Avoids having the interrupt re-raised multiple times until the reading
+    task finally wakes up.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 572e1664938c7ce6c308b36779f6a412959e03f1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 23:11:27 2012 -0700
+
+    altos: Track missed HMC5883 interrupts
+    
+    When it fails to signal conversion complete to the CPU, keep track of
+    that and report it with the rest of the current data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 79dbe1a5e46d7f0b8929400897631ab969cd2bc0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 23:09:16 2012 -0700
+
+    altos: Increase default STM stack to 648 bytes
+    
+    512 seems cozy given the printf implementation we're using and the
+    extensive interrupts.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aab7b31b71aa7c87c5a5003084e4b7773c30835f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 23:05:36 2012 -0700
+
+    altos: panic if MPU6000 self test fails
+    
+    Don't try to fly if the board isn't working right.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f9f65211c378849270a6138fda05ed2a166f7d82
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 23:04:25 2012 -0700
+
+    altos: Disable mag sensor for megametrum
+    
+    It doesn't work unless USB is connected or occasionally while the
+    debug port is enabled. It's mystic.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dd623b21cb904238c6d903b6936ff2f8ebf6f339
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 23:03:33 2012 -0700
+
+    altos: Allow megametrum to be built without using the mag sensor
+    
+    I'm having trouble getting it working reliably, so we'll like disable it
+    for now. This patch makes that possible.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bd21c050fd8b96b33ab6859c942bf55cf2b91868
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 19:47:52 2012 -0700
+
+    altos: Make profiling Viterbi decoder more useful
+    
+    This blocks starting the decoder until all of the data have arrived so
+    that the time spent in the decoder is easily computed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 407cefae9cb95c5910b3bd79851776c48729e06b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 19:45:22 2012 -0700
+
+    altos: Unroll viterbi state loop for >30% performance boost
+    
+    9.3ms vs 14.7ms, a clear win.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4f258fe565dc3e58b83761bfa1a2276946012163
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 17:18:57 2012 -0700
+
+    altos: Clean up STM I2C clock computations
+    
+    Fix both clock time and rise time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 84f9a525c64491afa9b7a565e3c10a4cee106e14
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 17:17:44 2012 -0700
+
+    altos: Clean up radio CRC handling
+    
+    Make the FEC code just set the CRC_OK bit like the cc1111 radio does;
+    eliminates a bunch of weird conventions across the FEC API.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b0b7f5da2d29716959c6793d744e47a3d435c247
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 14:38:35 2012 -0700
+
+    altos: get MPU6000 I2C link working reliably
+    
+    This slows the i2c bus to 100kHz (yuck), sets the rise time to spec
+    (it was way off) and adds more delays during bus setup. I've run this
+    for hours now without trouble. Will try to adjust things back to fast
+    mode and see if I can make that work as 100kHz isn't fast enough to
+    reliably get data at 100 samples/sec.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e63d624f9670b5e2e002fcd5f24b80cf7f1effdf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 14:35:56 2012 -0700
+
+    altos: reorder stm USB state stores to avoid races
+    
+    Must set ao_usb_in_pending before telling USB about new data or an
+    interrupt could arrive at the wrong time to clear it.
+    
+    Same for ao_usb_in_flushed.
+    
+    Without these changes, I've seen the USB bus lock up on occasion,
+    waiting for an IN packet to consume data, but with no IN data pending
+    in the hardware.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 08a4ed8fe794a2b2b52147bd5535fe0954822e95
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 14:34:53 2012 -0700
+
+    altos: include targe SPI speed in get request
+    
+    STM needs it to be provided when enabling the SPI device, so just fix
+    AVR and cc1111 to do the same.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a1d4a557a30e2e743936b828b654187ec562ca8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 02:46:27 2012 -0700
+
+    altos: Wait for i2c START condition before setting interrupt bits
+    
+    This seems better than the random loop that it replaces, but I still
+    have no idea why this is required; it doesn't coorespond to the docs
+    at all...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9695a217e13f9d194b6dc40e2696017e5f8e8176
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 01:22:32 2012 -0700
+
+    altos: disable FEC debug on MM
+    
+    Seems to work; we'll leave the code around in case something bad
+    happens later.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b96eece8e42df0713fc92d47b6bb27604c0168a2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 01:22:00 2012 -0700
+
+    altos: Clean up cc1120 driver a bit
+    
+    Make some variables static, remove stale debug code
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a00aff5ee93ea9763b5c0466fcecc823cad135ef
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 01:20:43 2012 -0700
+
+    altos: Don't try to grab radio while firing MM igniters
+    
+    If you're in idle mode, you stop forever as the packet mode receiver
+    will own the radio mutex forever.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8fc643c9768f0db31a248331681af9490f5715af
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 01:19:05 2012 -0700
+
+    altos: stm i2c debug code was calling flush() even when disabled
+    
+    The fancy stm i2c debugging code had calls to flush() that were
+    invoked outside of the conditionals leading to all kinds of fun --
+    flush() may re-enable interrupts, yield or do all kinds of wacky
+    stuff, none of which is appropriate from the middle of a device driver
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0285696e5280fc64774b6c3a2fcdaa36bd36ae7c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 27 01:17:51 2012 -0700
+
+    altos: mpu6000 requires a delay during start
+    
+    I have no idea why this is required, but the mpu6000 will not come up
+    and run if this isn't present.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4847595e0383e5ff9c5a373f3c7f2af8c210c50a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:25:00 2012 -0700
+
+    altos: ao_ignite.c is no longer cc1111 specific
+    
+    Move it to core
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8efac8eb99a9aabb45d9fbf742e4be91e4b331a5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:21:04 2012 -0700
+
+    altos: Add debugging code to check for stack overflow
+    
+    Stack overflow often happens from interrupt handlers sitting on top of
+    a task stack. Check for this during ao_wakeup as that is often called
+    during interrupt processing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ebeac02a990da3fa6dd71487141d0bc6f78b42de
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:20:17 2012 -0700
+
+    altos: Enable full flight computer functionality in MegaMetrum
+    
+    This turns on everything that currently works
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fb2b83fcd28199d8c686e676d46d6ecfbf706f37
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:18:44 2012 -0700
+
+    altos: Create a 32-bit 1MHz timer for use in profiling execution
+    
+    This provides a simple method for getting high-resolution timer data
+    to use in performance tuning code. It's not used by default anywhere.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 097b337eb9b7deff13d5dcdafddec9bec3868b93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:17:00 2012 -0700
+
+    altos: Abort radio receive when using flash memory on megametrum
+    
+    Radio receive camps on the SPI bus, making it impossible to access
+    flash memory. Abort any pending receive operation when trying to get
+    to the flash part.
+    
+    Yes, this is a total hack.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1ae69a1c2ce7e45db9d9c175bc63867eff68ebe5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:15:54 2012 -0700
+
+    altos: Make gcc happy with ao_telemetry_set_interval
+    
+    Using unsigned locals made GCC sad as it was compared with a signed value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c4036bf6e7997b618f89a05dd1214c16066ba2b2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:14:13 2012 -0700
+
+    altos: Turn radio-related bits of ao_flight_mm.c
+    
+    Now that MM has full radio functionality, we can make it work right.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b5f5fd92109ac6f4909a81303d52bc9220cc2520
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:13:14 2012 -0700
+
+    altos: custom hex printer for telemetry packets
+    
+    Using printf is way too slow with pdclib; just hand-write hex byte output.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f1ae622eff60e05c1f5d8f822a3cf6a85750c6cc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:11:10 2012 -0700
+
+    altos: Optimize FEC encode and decode
+    
+    Integrate interleaving, CRC and padding within the decode/encode
+    functions.
+    
+    Provide for ISR priorities so that the 1120 RX interrupt takes
+    precedence over the other interrupts or we risk losing bits.
+    
+    Optimize the viterbi decoder a bit (goes from 10ms per packet to 7ms
+    per packet).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 936ecad62596f34773afb7460b10f63df7d0896d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:07:38 2012 -0700
+
+    altos: Add GPS logging code for MegaMetrum
+    
+    MM uses a different logging format with larger log blocks, so
+    restructure the GPS logging code to fill them up
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 33f8f7add27a02d24b0671da353b59762224c1ee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:05:42 2012 -0700
+
+    altos: Make cc1111 radio control functions static
+    
+    No need to publish these; they're all private to cc1111
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 03dc80d15a2f8fe9d7340351226dadd8bc3cfdb9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 23:01:58 2012 -0700
+
+    altos: Clean up usage of port parameters
+    
+    Make stm port parameters always be pointers; this avoids the confusion
+    where some macros took '&port' and others took a bare 'port', and also
+    unifies code to run on other processors in a consistent fashion.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f11f05c5d634de2a80c34d0d3dc93925980f52e6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 22:20:50 2012 -0700
+
+    altosui: Make libaltos recognise new USB ids
+    
+    libaltos has a small range of 'AltusMetrum' products to avoid opening
+    other devices. We've got more IDs, so open up the range.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9dcb4e2ab60ecf0cc7371c1b1a620be952fa8776
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 22:19:01 2012 -0700
+
+    altosui: AltosSerial and AltosLink both tried to provide frequency setting
+    
+    AltosLink owns all of the device configuration, so remove that from
+    AltosSerial and make sure that AltosLink provides the right function
+    signatures (wasn't using the new direct frequency setting command).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eab18714ed9eabbcef0ff81b07427da042a58ccc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 26 22:16:44 2012 -0700
+
+    altos: rename ao_viterbi.c to ao_fec_rx.c
+    
+    Keep it parallel with ao_fec_tx.c
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 09761fe0f6ed40ff74317fbb47d6a74068fb4ce4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 25 06:51:36 2012 -0700
+
+    altos: Incremental viterbi decode
+    
+    Decode radio input one interleave block at a time. This overlaps the
+    decode computation with the packet reception, leading to lower latency
+    in an attempt to keep up with the transmitter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 628076aa90e7bc9a894646e417dd8e1fe149b60d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 25 06:38:34 2012 -0700
+
+    altos: decode cc1120 received packets
+    
+    Call the fec decode function, compute RSSI and check CRC
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 70cf32e89df19bde5185339fc703532c8a5b8be6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 25 05:03:34 2012 -0700
+
+    altos: Get cc1120 packet reception working
+    
+    Interrupt-per-bit, but it seems to work
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 246174b32bb6cf827d240c32d6a51c3513a08c37
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 25 05:03:16 2012 -0700
+
+    altos: Forgot ao_telemetry.h
+    
+    Not much builds without this...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b292c14790fc225029cba3f80ce8ad6c5652bc4e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 23 16:05:42 2012 -0700
+
+    altos: improve FEC apis to reduce data copying
+    
+    Integrate interleaving and whitening into encode and decode steps.
+    Add CRC checking function for receive.
+    
+    Make ao_fec_test program round-trip the data and verify correctness.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 74f6a1a8c8fa9d5bb8d74c99782310b431dd4727
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 23 02:24:30 2012 -0700
+
+    altos: Add ao_viterbi.c to megametrum build
+    
+    It's not used yet, just wanted to see how big the resulting object
+    file was (492 bytes).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 566a0c277de01963922cabc80db8ec3a129923bd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 23 02:23:08 2012 -0700
+
+    altos: fix comment about decoding last byte of FEC data
+    
+    There aren't *any* forward bits to use when decoding the last byte.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 83549f8bde42c3fddbdc817540c869dc8aefd013
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 23 02:13:52 2012 -0700
+
+    altos: Make ao_fec_tx_test build cleanly with -Wall
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f7bf07dfdad260c1f219064957ef08fb480bf20f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 23 02:12:58 2012 -0700
+
+    altos: optimize Viterbi implementation
+    
+    Minimize data usage, make data arrays static
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ff8de3af193839de4bacfd07ade7a5f9ac0bf5b3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 23 00:54:42 2012 -0700
+
+    altos: incremental viterbi decode
+    
+    Decode bits incrementally. Don't bother decoding the last byte; it's
+    always a pad byte.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 047e95421c87c5d056038797b48f759bedabf245
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 22 23:31:11 2012 -0700
+
+    altos: Start optimizing viterbi decoder
+    
+    Only need two cost arrays (previous and next). Create constant
+    full-width decoder table instead of expanding bits into bytes for each
+    decode step.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cbf79a0f9cb859d04e8e03d627219cb2bf49611f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 22 23:12:02 2012 -0700
+
+    altos: Add the simplest possible viterbi decoder
+    
+    I think I understand how it works now. It's not exactly speedy, and it
+    uses a lot of memory.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 75e4521ec42a368cebc67a07f8713d7a854ea265
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 21 09:52:37 2012 -0700
+
+    altos: Move FEC code to core
+    
+    It's not a driver as it's not specific to the 1120 chip
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0b5548d6ced67201311e1072d37fbedd3d9929c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 21 09:51:17 2012 -0700
+
+    ao-tools: Support MM telemetry packets in ao-telem
+    
+    Parse the new packet formats
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6f421818fd7062f03bfaf9e606d6a4cfdcb13b49
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 21 09:50:18 2012 -0700
+
+    altosui: Support MM telemetry packets
+    
+    Required restructuring the whole telemetry system to provide abstract
+    interfaces to flight data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ff5b0ba90e73a83360a2e8a7e9969ed2c3ce1514
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 21 09:46:50 2012 -0700
+
+    altos: Crank down STM SPI speed for MM
+    
+    The cc1120 is noisy enough to break SPI data transfers at 4MHz, so
+    crank things down to 1MHz. It's "stable" now, but clearly needs a
+    filter and shorter traces.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d2bd95edb6f77daeb1e8f043c4a239c248728e0c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 21 09:45:42 2012 -0700
+
+    altos: Add full MM telemetry
+    
+    Create two new telemetry packets to hold all of the MM data.
+    
+    This patch also splits the telemetry structures out of ao.h
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 419a801131c1034f1fa149a67850290431cbda72
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 21 09:39:10 2012 -0700
+
+    altos: Configure STM LCD driver for giant LCD digits
+    
+    These devices require static mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 611f37607fadcdc9908d67456f844a452ad4a87a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 19:11:35 2012 -0700
+
+    altos: Make sure ao_storage_config is set before reading config block
+    
+    ao_storage_read does in fact call ao_storage_setup, but we need the
+    value of ao_storage_config *before* calling ao_storage_read, so call
+    ao_storage_setup first.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ae3662c56effda9f0516c7d6ffd2d5f56b859593
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 19:06:08 2012 -0700
+
+    altos: hack STM serial number to 58
+    
+    otherwise altosui won't record telemetry
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 976a8375932ddb46ca3100863b0a892732c0923e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 19:04:53 2012 -0700
+
+    altos: make cc1120 ao_radio_send re-entrant
+    
+    It gets called from multiple tasks, so put local data on the stack.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 629f43e7c7abbff33e14b168a08a4b6a9c88b937
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 19:04:22 2012 -0700
+
+    altos: Add telemetry to megametrum
+    
+    Now that the radio works
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9b24f413da0b6d989b32e8654a91c8deee4c81dd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 19:02:50 2012 -0700
+
+    ao-tools: add rudimentary support for MM telemetry to ao-telem
+    
+    Just pretends they're TM packets for now
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4cb46b8a84a0dd5b8fcb479d7aa5157480e1bc67
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 19:01:24 2012 -0700
+
+    altosui: Add rudimentary MM support to altosui
+    
+    Decoded the MM sensor packets as if they were TM packets.
+    Add the USB ids.
+    Add class of 'altimeter' devices and match those instead of just
+    telemetrum as appropriate.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 55747ce210d7d80d5b4fdaaf9dc7ee0f7bc8b0a3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 18:58:56 2012 -0700
+
+    altosui: Move product definitions from AltosUI to AltosLib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e6d236fdc615625fbbf28377453f920729e49b0f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 16:17:00 2012 -0700
+
+    altos: Software implemenation of CC1111 radio encoding
+    
+    Add CRC, whitening, FEC and interleaving routines for transmission
+    path to allow cc1120 to send telem packets to cc1111.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a294852b3607947f0f86bf236785456d8719e5f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 16:14:33 2012 -0700
+
+    altos: Be more careful about register save/restore in ao_yield
+    
+    Make sure the general registers are all saved before messing with any
+    of them. Then, explicitly use r0 to save/restore apsr and primask.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9d3fe2a80d0925e3eded6d738d05c5b4ea61504c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 16:12:18 2012 -0700
+
+    altos: Don't lose IRQ disabled state in ao_sleep
+    
+    Using ao_arch_critical around the wchan setting will force interrupts
+    to be re-enabled before ao_yield records the state of that bit,
+    potentially causing problems with functions not atomically testing and
+    sleeping.
+    
+    Tasks that need to set wchan with interrupts disabled should have
+    interrupts disabled when entering ao_sleep already.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4e6d96816e6604ee8d9bb49345a1c1211699a655
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 16:11:23 2012 -0700
+
+    altos: ao_storage_read already calls ao_storage_setup
+    
+    No need to call twice.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca310342d7b0bd1b78318cae38d920b8690dfd36
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 16:09:43 2012 -0700
+
+    altosui: Catch timeout errors when setting up TD telem monitoring
+    
+    Close the port in this case so it can be used for other things.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 58e005375ca29dec6091d87159055004e7f19605
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 12:43:43 2012 -0700
+
+    altos: Use interrupts to wake up after RDF transmission.
+    
+    Also clean up the debug output
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5df94f74522357e062f4ec2786ff825381b2fb10
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 17 12:42:32 2012 -0700
+
+    altos: Crank cc1120 power down to 0dBm to avoid crashing CPU
+    
+    Looks like RFI from the transmitter is confusing the CPU; lower the
+    1120 power output from +14dBm to +0dBm to keep the CPU happy.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e856df474c386b8df3d2bd9e87b766ae0439efbf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:41:17 2012 -0700
+
+    altos: Reduce STM SPI data rate to 4MHz
+    
+    cc1120 doesn't want more than 6.1MHz, otherwise it gets very angry.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e09e35471e788b88909ff01037fb8e0e4eabcd7d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:40:30 2012 -0700
+
+    altos: Start making cc1120 radio work
+    
+    RDF tones and radio calibration work now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c2949ea15c59215834fedac7646f50c8a09f716f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:38:37 2012 -0700
+
+    altos: Fix cc1120 packet mode datarate and config
+    
+    Was using the wrong function
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1b7e4c29bf9608bfc972ae28b53cc823f4c37f92
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:32:10 2012 -0700
+
+    altos: Make sure cc1120 is initialized correctly at startup time
+    
+    Check to make sure it pulls down MISO when CS is enabled.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 27c95adf35e646840b9bd562497eea0dc96bb9bb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:31:36 2012 -0700
+
+    altos: use 'b' command for radio beep
+    
+    'B' is used by the baro data dumper.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93e6d0a2c0b60e7bedd34f06ba63b468dcc8e013
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:30:03 2012 -0700
+
+    altos: Fix RDF mode data rate and PKT_CFG0 value
+    
+    Changes tone to the desired 1kHz frequency and ensures the PKT_CFG0
+    has the right value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c96e60bedeb00d28c36436c12b803fd8cbadce26
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:28:55 2012 -0700
+
+    altos: Actually write cc1120 register values
+    
+    Helps to not use 'read' mode when writing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97d163c88ed8c8f64a9714018863d0b6eedab38f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:27:48 2012 -0700
+
+    altos: Configure cc1120 frequency
+    
+    Set default conversion value, and pull the selected frequency
+    calibration data out of the config block.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a5a7df405c242593cbc828d5b66bbfc141a35947
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:26:06 2012 -0700
+
+    Temporarily disable packet and telem on MM
+    
+    While work on the radio code progresses
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit baf1be1def01266512e67068948bf19b04ead6aa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 15 22:23:10 2012 -0700
+
+    Allow product to override maximum number of command lists (NUM_CMDS)
+    
+    Just in case some product wants to save memory, or have more.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bfbd1d82f4c25120a97840a1bd4787680823afd8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 5 15:15:25 2012 -0700
+
+    altosdroid: Add files necessary to build application
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b8c363d9411fd5e79e3f806894dbc12bcc106b88
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:56:25 2012 -0700
+
+    altosui: More changes to migrate code to altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5634192a6036c6c7b47d224e2988e81bb71a4557
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:55:33 2012 -0700
+
+    altosui: attempt to get ms5607 data into .mega files was misinformed
+    
+    The ms5607 'p' command also means 'go into packet mode', which nicely
+    broke attempts to communicate with teledongle
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 705891be53e298ac6ced4ba02b87d2f6d1085b34
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:54:36 2012 -0700
+
+    altos: Make cc1111 products all depend on ao_arch.h and ao_arch_func.h
+    
+    Ensures that files get recompiled as needed
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9ad342ae4c34626df4f2891da4c7f6d2c14d73e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:53:53 2012 -0700
+
+    altos: Other half of the ms5607 prom reporting patch
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dec5cbee22f13c47690b0c6bf7ca724ef132fe5e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:53:06 2012 -0700
+
+    altos: typo in ao_monitor prevented ground station from including code
+    
+    This created ground station software that couldn't actually receive
+    and report telemetry packets.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e687a9bafc696998b47fd0300fbc89dece09509c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:52:38 2012 -0700
+
+    altos: Make 'v' command dump pressure sensor ROM values
+    
+    Avoids needing a new command
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1832e2f76c844e97a2bd11226b003fb2af8057db
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:51:35 2012 -0700
+
+    altos: fix test builds of non-accel flight code
+    
+    Remove references to accel variables when building baro-only flight
+    test code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 06afa2c3e78ea5bc9f1eb4913ee35c0eab0ac1bf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:50:10 2012 -0700
+
+    altos: Legacy telemetry needs original ADC record
+    
+    The legacy telemetry packets included the raw ADC structure directly,
+    so make sure that doesn't change further, allowing teledongle firmware
+    to remain compatible with old TM firmware.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f1b14932149153a096961fff94191778f88581d9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:49:15 2012 -0700
+
+    altos: Remove unused AES code from teledongle and tidongle
+    
+    Leave this out as neither of these products need it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 718a8affe1df98eacfd707b5c8c34f9456dcff14
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 20:47:14 2012 -0700
+
+    altos: Remove accel_ref from pre v1.1 TM firmware
+    
+    V1.0 needs RAM space for flash buffer, leaving too little room for
+    this extra data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7a19d6790a9800f925c8de24aac71796351e2c04
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 4 19:28:58 2012 -0700
+
+    altos: More cleanups for moving files to altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f86dac643081987c8994ab57a96640d5e91b342a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 19:59:40 2012 -0700
+
+    altoslib: Clean up random rebase failures
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dd43a2ae7594f062a8980d1756a07488ee54b447
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 19:42:47 2012 -0700
+
+    altoslib: Move new sensor library code into altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0772020c969a69c3b0a705de7362340a9732daab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 16 20:27:45 2012 -0700
+
+    Build altosdroid
+
+commit e81163ed875dc93a618baf9278f43ed7dd0f730e
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Feb 23 16:43:04 2012 +1300
+
+    Add local.properties to .gitignore
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 392c878000e9909d37dae6342df3d6cb8f217a1b
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Feb 23 16:41:26 2012 +1300
+
+    Add TelemetryService.java and associated files
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 69e6df07976a56b49e07c242cd6e5b2cbd2a578d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Feb 23 17:00:48 2012 +1300
+
+    Move altoslib sources to top dir
+    
+    No sense having them live deep in the file system.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9b659904109f992b8a3e61efb94e81cdb19af1c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Feb 23 16:37:24 2012 +1300
+
+    Demonstrate using AltosLib from altosdroid
+    
+    Get things hooked up so that we can use AltosLib functions from the
+    android application; it's a bit of a hack at present, but appears to
+    work. Some more 'official' technique would be nice...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a018724e40f2a4c0bae8b3d5c77bb90328ad4314
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Feb 23 11:15:23 2012 +1300
+
+    Ignore generated altoslib files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 025eb09b5de9b50de143da9f36bc02818e018ba9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Feb 22 23:39:01 2012 +1300
+
+    Build Android local.properties from local.properties.in
+    
+    Make sure the SDK path is set correctly
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3a80545d4ecedc4b98a9ee8296ab6abbbf64312d
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Wed Feb 22 23:40:25 2012 +1300
+
+    Add $HOME/android as an SDK location
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 091affece185dcd0832a55b0befeacaa182a57bb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 13 10:40:30 2012 -0800
+
+    doc: Add companion SPI message protocol doc
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bb5b5312a0c6102b12f3d4710ef213f0f6c67412
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 7 20:56:49 2012 -0800
+
+    altosui: Clean up a few 'fat' build rules
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fc86ae58c3a296730fa99010cc27b8b3c2c3b780
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 5 16:30:41 2012 -0800
+
+    Add altosdroid/Makefile.am
+
+commit dfa059b22bf15de3f25328aeef4fdb8e5ca664dc
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Wed Jan 4 21:01:44 2012 -0800
+
+    Add AltusMetrum Logo as app icon, and change app name
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 402f1e76909229fc0c3b54743ba577b657495faf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 4 20:54:40 2012 -0800
+
+    altosdroid: build android bits when possible
+    
+    Locate android sdk automatically
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a48e4d40729e736929632ec422fd189ecdfba33b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 4 20:33:06 2012 -0800
+
+    altosdroid: import code from mjb
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 81465a20049da40cd8d3cda920d6585ffe87bfe3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 4 20:28:42 2012 -0800
+
+    altosui: Move java altoslib to top level
+    
+    This will be shared with other (android) java code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d6df16525927d8096d1c0cccf4c86bf4c6599d53
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 22:43:48 2012 -0800
+
+    Add altoslib/.gitignore
+
+commit 02b53b7f592b78b2fec4f4a17b6b3e114d2bf3c5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 22:42:18 2012 -0800
+
+    altosui: Fix AltosTelemetryReader move
+    
+    Lost the provided link value causing a crash.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9fb15d397890c7e78bf3c1438f142f62bfc2bd35
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 22:41:48 2012 -0800
+
+    altosui: Remove unused files
+    
+    Left around from development, these aren't useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5270a0f1416baef5fde08547c6c98d97f973ae95
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 22:35:41 2012 -0800
+
+    altosui: Move telemetry reader &c to altoslib
+    
+    Move all of the device and file reading code into altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93305717ac4c993c88d9144d797ca64d26db97c5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 22:13:38 2012 -0800
+
+    altosui: Move AltosState.java to altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a5ac5c37ea385e3a2b2703c6f125b4e3b55e867a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 22:05:47 2012 -0800
+
+    altosui: Pull most of AltosSerial into AltosLink
+    
+    Share basic command processing across java users
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 18914b9a84bbd8c4364a1568bb07dcc2b04ad7ba
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 21:12:45 2012 -0800
+
+    altosui: Move AltosGreatCircle.java to altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 027863b737190bccc3b5cd032d77587396d0c5c4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 21:08:34 2012 -0800
+
+    altosui: Move AltosEepromTeleScience.java to altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 346df410f570a67cda057550a067fa2b451b785d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 21:05:02 2012 -0800
+
+    altosui: Move more eeprom stuff to altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a4ccdd253a9873c16f194a63a79f0c26feaafa29
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 20:57:57 2012 -0800
+
+    altosui: Move eeprom managment code to library
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 89aa06cfdcb02de1894ccb01aed97782f9eec9b2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 20:47:16 2012 -0800
+
+    altosui: Move AltosEepromChunk.java to lib
+    
+    Also fixes install issues with split lib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b273b8b298540b1a6d0a87b1cf61df1fbf62e013
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 20:39:16 2012 -0800
+
+    altosui: Finish moving AltosConfigData to altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4c88b0ca96758b663c82395e63b338043d1c1a10
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 20:34:38 2012 -0800
+
+    altosui: Move AltosConfigData.java to library
+    
+    Create a new 'AltosLink' which exposes how to talk to the remote
+    device abstractly via 'get_reply' and 'printf' methods.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ead8f1cfca2c454d18dce56479899f2b423d8bdd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 20:32:35 2012 -0800
+
+    altosui: Add back in the split-out Altos constants as AltosLib
+    
+    These were pulled out of Altos.java, but not added back to git
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3c2f601139d36761de6a8a2210545d082ef16133
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 17:26:59 2012 -0800
+
+    altosui: Complete split out of separate java library
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 40ee170753f4fd422c848e34a8da104683b7c8a2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 16:16:51 2012 -0800
+
+    altosui: Clean swing/awt bits out of altoslib
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 71636c1ed7cbe075921391605d1ac4edaa6ee52b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 16:13:46 2012 -0800
+
+    move a file back
+
+commit 6510e8495fc5e8057b6092963def4d78978625a0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 16:09:29 2012 -0800
+
+    altosui: Split out lots of the altosui code to a shared library
+    
+    To be shared with the Android application eventually
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97663f922e236f4ee7bd08277ca80d419b5cd10f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jan 2 15:45:14 2012 -0800
+
+    altosui: Split out UI-specific preferences
+    
+    Prepare to create library shared with android application.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c9e52287751867d9e451146ccde78109609d30d7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 19:06:08 2012 -0700
+
+    altosui: Fixed width format for new IMU values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9aa7993ee31bdfd6890ad7262a0375c07464ee76
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 17:09:00 2012 -0700
+
+    altos: Intgrate hmc5883 sensor into adc ring
+    
+    Creates a task to poll the mag sensor and place the data into the
+    sensor data ring.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 69a8907ecbb7ca0e8526aeea0dc7490a191a0f8b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 16:57:22 2012 -0700
+
+    altos: Get HMC5883 driver limping along
+    
+    Not pushing data into the ring yet, but the chip appears to work now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97317d332f21c42860747c4ecde633bd0228ef52
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 16:56:41 2012 -0700
+
+    altos: Reset i2c controller at boot time
+    
+    In case it's wedged.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 64e2e66a5239541b15f43172655cfb3560bec79b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 16:54:42 2012 -0700
+
+    altos: Fix broken EXTI edge mode selections. Clear pending exti on enable
+    
+    Make sure the edge mode registers are set according to the requested
+    mode.
+    
+    Clear any pending interrupt when enabling to avoid spurious isr call
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8d425ffba84ec6f632e8c0d44105de73242a86a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 16:53:46 2012 -0700
+
+    altos: Route correct GPIO line to interrupt controller
+    
+    Which GPIO a particular pin interrupt comes from is selected by the
+    SYSCFG EXTICR registers; set these when an exti interrupt is configured.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1353b277f8314fbddef81c743bd6ea229364fd18
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 2 14:58:00 2012 -0700
+
+    altos: Enable some debugging during flight mode on MM
+    
+    Until we've got the radio working, there's no way to see inside the MM
+    state without using USB. Add a diagnostic command to dump out the
+    internal flight state variables.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c04af7533bd3fd3f3260338c0753fde966131720
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 1 23:07:38 2012 -0700
+
+    altos: Add support for MegaAccel daughter card.
+    
+    Switches all acceleration computation to using the MegaAccel
+    accelerometer to ensure support for high-g flights.
+    
+    MPU6000 values continue to be logged as normal
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1824761f5b98e92485e2dd347b1c4d043ec207e2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 1 19:51:25 2012 -0700
+
+    altosui: Quick hacks to download megametrum data and convert to CSV
+    
+    Very little useful data crunching is done, but at least we can save
+    and convert files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ab85337aa942cb73a08bd3b783771179773b9a67
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 1 19:35:33 2012 -0700
+
+    altos: Timers clock base depends on perhipheral bus prescalers too
+    
+    For some weird reason, a non-unity perhipheral bus clock scaler
+    affects the base of the various timers; this left the 100Hz tick
+    running at 200Hz.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ff4f3a56e09d595abbe32293cbdf1fe368633c49
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 1 19:35:01 2012 -0700
+
+    altos: megametrum has logging
+    
+    This makes sure the various other subsystems know about it, like the
+    'v' command.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6806103bad98385c0ec122d400f981eb81c59dd3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 1 19:34:17 2012 -0700
+
+    altos: add high-z accel and mag sensor to megametrum logging
+    
+    These nicely fill the 32-byte sensor log record
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e5aebfe0203de9e69712fac291c8cd0d3a96a385
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 1 19:33:18 2012 -0700
+
+    altos: Mark full and mega logging correctly in 'v' command
+    
+    Make sure megametrum reports 'log format 5'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7fce3ddf5e7e92a14cefb7fcf35e4014df744987
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 28 11:48:04 2012 -0600
+
+    Bump to 1.0.9.5
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 47bc9b2a39b7a8d3ef3fe8acc7fbf0512695e548
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 28 11:47:28 2012 -0600
+
+    altos: Clear stm i2c transfer timeout alarm
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8164cd95db62e4564b3a9ba5c06a74c870c03841
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 28 11:46:51 2012 -0600
+
+    altos: Remove debug printf from accel auto-cal
+    
+    Otherwise, this goes way too slow
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ee61fb8ccb47f94b7c39e803f5a0248840d1eea6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 28 11:45:53 2012 -0600
+
+    altos: Clean up ADC selection for cc1111
+    
+    Depend directly on product defines instead of trying to guess
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2df9113a22f4f67707d9ee777bd6b23ff671b105
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 28 11:45:30 2012 -0600
+
+    altos: All cc1111 products have a radio
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a53b8b84283eb62157b0f8ecd8061f61a4b6bd6f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 28 11:44:45 2012 -0600
+
+    altos: Shrink telemetry generation code
+    
+    otherwise TM doesn't build anymore
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 43e558738c76afd72fc01660884be3158f44470d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 28 11:44:07 2012 -0600
+
+    altos: Try to get hmc5883 working
+    
+    No joy yet
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9f5e4cf7d8204016e023cf439d93de1203dc406e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 18:23:39 2012 -0600
+
+    altos: Make teleterra v0.2 compile with new ao_data struct
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5dd5e0284c8b26b0d4ce69c64bb7864e0ae83db7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 17:45:09 2012 -0600
+
+    altos: Make telepyro-v0.1 build with new ao_data struct
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 29edf6d901432a1afc65900ff599c963edac5a2b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 17:44:01 2012 -0600
+
+    altos: Make telescience-v0.1 build with new ao_data struct
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5fd869b244f8f2b76258dc31a5507a73fd47cf1d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 17:31:12 2012 -0600
+
+    altos: Make telenano build with new ao_data structure
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 627b904b36e129ff2ead436a626699abfc3b3211
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 17:29:27 2012 -0600
+
+    altos: Make stm-demo compile with new ao_data structure
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 090dc9aecdf4cfd1ac727325ae141d441c5b28aa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 17:26:43 2012 -0600
+
+    altos: Make telemini-v1.0 compile with new ao_data structure
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 98e992b65d366d9f79d7d2dd2dd4d1886dd1d9c4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 17:25:17 2012 -0600
+
+    altos: Make telemetrum-v1.0 build with new ao_data structure
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce8153472069ed56b24ac36f297ac569d1f767d4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 17:24:09 2012 -0600
+
+    altos: Make telemetrum-v1.1 compile with new ao_data structure
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ed635545e0b965901032ed2c3474ffe997c73de3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 16:52:45 2012 -0600
+
+    ignore pa to altitude conversion file
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dd73c9b441f7672fb9982c4caeb5178df30f5d2b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 16:47:30 2012 -0600
+
+    altos: Split out mm-specific versions of sampling code
+    
+    Avoid breaking telemetrum (too much) by splitting this stuff apart.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9eeba439ce8c9dc1def8528f96b6a67c6578d656
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 16:46:00 2012 -0600
+
+    altos: Don't start ADC ring until the other sensors have a valid value
+    
+    Yes, this is still an ugly kludge, but it's easy.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d01c10eff4b70af13347969a7cece8730cf1a3f1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 27 16:44:38 2012 -0600
+
+    altos: Data packet tick count does not live in adc structure
+    
+    It was moved to the global structure. Having two is confusing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 78423f3fc5164ea9fd428606419784c1700ad5c5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 25 23:18:06 2012 -0600
+
+    Get megametrum ready to at least log flight data
+    
+    Doesn't track flight state changes correctly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0239e4dfe587528524b6380bbf6d9583047e52d6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 18 21:23:47 2012 -0700
+
+    altos: Poll mpu6000 values every tick and stash them locally.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 34bb17bc1a3d8a1c95b5e57f059e7a1747e17a03
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 18 20:16:35 2012 -0700
+
+    altos: Finish ms5607 support
+    
+    This has the MS5607 polling once each tick for pressure and
+    temperature and then saving that in a global variable. The command UI
+    provides for dumping the prom data so that an eeprom file can have
+    raw sensor data along with the conversion factors necessary to compute
+    useful values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5d8b9d524d6424ff98dcc4155fe8b8bd892b6d8f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 18 20:04:57 2012 -0700
+
+    altos: Add conversion between Pa and meters
+    
+    To be used with the MS5607 which generates data in calibrated units.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1541fc0bde71f503b1ae5757497e9e1e6d023111
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 23:14:57 2012 -0700
+
+    altos: Check MS5607 CRC. Clean up MS5607 API
+    
+    It's not ready for flight yet, but at least it's sensible now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6a62edd4a1f01a6ee380c3aabaff3f437e8d6f1e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 23:14:14 2012 -0700
+
+    altos: Conditional byte swapping in mpu6000 driver
+    
+    Only needed on LSB machines.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6a973f788563ccc66b01cc7557a004dabef18d09
+Merge: d387f24 da2c920
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 16 09:13:53 2012 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit d387f246b24502642b76aad04eb3e0f1a5b78a05
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 16 09:09:44 2012 -0600
+
+    build-dep on default-jdk instead of openjdk-6-jdk, closes: #655580
+
+commit da2c920b9f3378d5a18551e008c1da5dace1e0ef
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 21:53:53 2012 -0700
+
+    altosui: Try to make telem tick counts match eeprom
+    
+    telem files can have an extra wrap or two of tick count if they start
+    recording a long time before the flight. Account for this so that the
+    CSV file output from each have matching tick values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b5b898264077fcada29e73efa28dcbe4729f2709
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 21:53:11 2012 -0700
+
+    altosui: Output recorded clock tick in CSV files
+    
+    This allows eeprom and telem files to be correlated accurately
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 73c26f39b1a08fcc13e23a5b1a4293bc7df9f163
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 21:52:12 2012 -0700
+
+    altos: Hacking at cc1120 driver
+    
+    Still doesn't work, but this adds a ton more register definitions
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 318b564486aa9965bbad54c71e51fcb32b414162
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 21:51:25 2012 -0700
+
+    altos: Get mpu6000 working
+    
+    This initializes the device appropraitely, and provides a command to
+    dump the current values in converted form.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit af949c67eeb9dc310b1430d3435d241adccfc0a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 21:50:26 2012 -0700
+
+    altos: stm: pass DMA buffer index to DMA completion callback
+    
+    This lets the user know which DMA has finished.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dd7699cf8daded17ac41abf5c5170cfb599b6ff5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 21:49:24 2012 -0700
+
+    altos: stm: delay during USB config with pull-up off
+    
+    This makes sure that a reboot will reliably cause the device to
+    disconnect from the USB bus.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 744d05e6037ffc11688a9faa9c7b5dcda4065ee3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 7 21:47:17 2012 -0700
+
+    altos: stm: share i2c_stop code between send and recv
+    
+    This waits for the stop signal to appear on the bus, necessary before
+    starting another transaction.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ddaf501ddcfc1a5f74a1ef1b6b76e1c82d89c77a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 6 23:54:13 2012 -0700
+
+    altos: stm i2c DMA for large recv appears to work
+    
+    Transaction appears to be clean on the i2c bus now; correct number of
+    bytes received, and the nack and stop at the right time. This tests >
+    2 length reads; should try that too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fad6cda3d081d438b18305611f37fe05335aa372
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 6 22:53:25 2012 -0700
+
+    altos: megametrum needs ao_mpu6000.h
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f66f5de7a6cae71948da777ad0638bc6267a710f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 6 22:52:56 2012 -0700
+
+    altos: Switch megametrum compile to -Os
+    
+    Looks like gcc has compiler bugs with -O0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0f0cc91ce8e9807dca48a5c0c53d821f5060e245
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 6 22:47:33 2012 -0700
+
+    altos: STM i2c work.
+    
+    Start now driven by interrupts
+    Send now done with DMA and interrupts
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 69feb1e3d94a028d04529edb015654bafd06353b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 1 18:12:41 2012 -0700
+
+    ao-tools: Add GPS and rssi printing to ao-telem
+    
+    This prints all of the basic telemetrum messages now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8b08095b3d41e21684a10bddfb54431019da5af6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 1 11:08:49 2012 -0700
+
+    altos: Report latest telemetry data, rather than using the oldest
+    
+    ao_sample_adc points to the *next* ADC entry, rather than the most
+    recent one. Step back one entry to get the latest valid data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7b0b6bcc40891d8dd106d091d3af8107b2941c66
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 25 23:29:20 2012 -0700
+
+    altos: Add hmc5883 and mpu6000 drivers to megametrum build
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e0b8c614ec4d11f432963e48d94e4497d31a9ddc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 25 23:27:58 2012 -0700
+
+    altos: Add mpu6000 and hmc5883 stubs
+    
+    No real drivers here yet, just some testing stubs
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0266e08dbf19e2204fb5f758d5d0f944d2afff7d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 25 23:26:57 2012 -0700
+
+    altos: Add STM I2C recv and stop funcs
+    
+    Recv doesn't appear to work with more than one byte
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 962476911aaab17fd482593a0e8b95fe47de2f29
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 25 23:25:43 2012 -0700
+
+    altos: Oopsed the STM DMA channels for I2C1
+    
+    TX is 6, RX is 7
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5f55d0490017faa19b8d70b1742e4a45266c7e79
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 20 22:26:16 2012 -0500
+
+    altosui: Mac OS Lion Java default heap space is too small (Trac #37)
+    
+    Increase it to 512M in the Info.plist file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1489263b895a2a825e29d0560c9b1dbba8a3f431
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 17 11:01:18 2012 -0700
+
+    altos: Starting to write cc1120 driver
+    
+    This does "something" in radio test mode, appearing to generate a
+    730MHz signal.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cc305ea231ae22278abf91c0d9925f5992945369
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 14 13:53:36 2012 -0700
+
+    altosui: Add apogee lockout configuration
+    
+    Provide suggested values of 5/10/15/20 seconds.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b4ab9f5dfa9f2a50e0528acf0a1fdeaa1f9bc523
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 14 13:52:37 2012 -0700
+
+    altosui: Read frequency from device while configuring ground station
+    
+    Missed this when adding the TD config UI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5c8af6d35ebfc8fd896dfbf9928ec8f9dbfa531f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 14 12:05:28 2012 -0700
+
+    altos: Remove debug printf from cc1111 check-stack script
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6cb744e305116a738b5d71686c6748f6f08c12ea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 14 12:02:14 2012 -0700
+
+    altos: Respect apogee lockout time in flight algorithm
+    
+    This prevents any apogee detection from occurring until the specified
+    number of seconds after boost. This also prevents the switch from
+    accel+baro to baro only mode in the Kalman filter.
+    
+    The test frame work is also changed to look for Apogee lockout: in the
+    eeprom input file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9a8fec1b6d8f3346f988882ffb03d7d0e45b3c81
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 14 12:00:32 2012 -0700
+
+    altos: Clean up test scripts
+    
+    The flight test scripts were using invalid bash syntax. Clean that up
+    and also switch the default flight directory to my new
+    ~/misc/rockets/flights directory.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e9f6fca7cfe796cbd86ae9d8f1ebe31bba7251db
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 13 18:38:29 2012 -0700
+
+    altos: Correct STM USB driver
+    
+    Configure endpoint registers correctly now.
+    Restructure code to make sure we send the right IN packets.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a84d34fa08f43a5f79b1a5f8d8de7674d04647d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 13 18:35:46 2012 -0700
+
+    altos: Check all USE_SERIAL_*_STDIO when computing AO_NUM_STDIOS
+    
+    AO_NUM_STDIOS is allocated based on the total number of possible stdio
+    values. Now that multiple serial stdio are possible, make sure to
+    check all of them.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3c67543f3e5fddc6a8850f33ac519ccd55b607f0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 13 18:34:52 2012 -0700
+
+    altos: Set USB Interrupt endpoint interval to maximum allowed
+    
+    We don't ever send anything over this channel, so there's no sense
+    asking the host to poll it very often.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0667261e03b3fd403e97d02ea6204b007bb13f58
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 13 09:39:20 2012 -0700
+
+    altos: Pull more interfaces out of ao.h and move to separate files
+    
+    This moves the aes, btm, companion, lcd and packet interfaces out of ao.h
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2b4a53342980618b193cbee5b803e56f7c937893
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 13 09:36:00 2012 -0700
+
+    altos: ao_telelaunch.c got left in src directory
+    
+    Should live in product/ao_telelaunch.c
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 040a6eb119451026e1ec7c3a6a8e76b439c632d5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 12 14:51:07 2012 -0700
+
+    altos: Massive product config cleanup
+    
+    Support multiple serial ports more cleanly
+    
+    Split out parts of ao.h into separate feature header files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f952f9c285e2718a433c8c720c9b5d9c369e7036
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 12 14:50:12 2012 -0700
+
+    altos: Start adding apogee lockout support
+    
+    Remove radio channel support too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6d1606895f70c6bca20c25084107f90bd0b613ec
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 11 23:31:28 2012 -0700
+
+    altos: Switch stm-demo to HSE clock, add USB
+    
+    Requires that SB17 be soldered shut so that the MCO from the STlink
+    CPU is available the target for HSE input.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 49ac2828510e8b5fcba7e31631dac0580a455011
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 11 23:30:13 2012 -0700
+
+    altos: Rework how STM clocks are initialized.
+    
+    Clean all of the RCC configuration up after turning on the clocks.
+    Use the MSI clock during initialization to avoid messing around with
+    the HSI clock temporarily.
+    
+    Allow for an external clock on the HSE line.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93ef7e4a23d13540615f1a9782e1d58ddb7f258e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 11 23:29:38 2012 -0700
+
+    altos: Use new USB driver in megametrum-v0.1 product
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d432307a3c2709634350eaa1262b935028d073d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 11 23:28:45 2012 -0700
+
+    altos: Add STM USB driver
+    
+    Emulates the usual CDC-ACM device
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit afd52a8b54fe31577d939a161ea9cf5ad48e3a43
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 11 23:26:30 2012 -0700
+
+    altos: Add STM ao_arch_reboot implementation
+    
+    Uses the AIRCR register to force a reset
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 150881f86f8d90b5867507889441990d18223e62
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 11 23:25:36 2012 -0700
+
+    altos: Add register definitions for STM syscfg and usb
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0dd9e1dd62656a931f9559af6da9131f704f83f9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 23:27:43 2012 -0700
+
+    altos: Add support for multiple SPI busses and sharing device drivers
+    
+    The STM32L151 has several SPI busses, and we want to use more than
+    one, so add a 'bus' parameter to the SPI interfaces. To avoid wasting
+    time on AVR and CC1111 processors which only use one SPI bus, elide
+    those parameters from the actual functions by wrapping them with
+    macros.
+    
+    Configuring chip select is now all macroized so that each chip can
+    have its own version, allowing the STM to share the various SPI device
+    drivers with the cc1111 and avr processors. Note that only the M25
+    driver has been ported; porting the others is 'trivial', but not
+    necessary at this point.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 35e3c47da895bdd868b9b66b98bca64bd82db862
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 22:25:59 2012 -0700
+
+    altos: make megametrum beeper test keep beeping
+    
+    Used to measure the beep frequency.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9804528e249db256e020d4b5340ba6216d3474f0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 22:25:13 2012 -0700
+
+    altos: Check for cc1111 flash overflow
+    
+    The linker is supposed to do this, but it ignores the static
+    initializer data added after the code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d15c4976ed9c23c861e620eb9c429d1cb7eedbee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 22:24:36 2012 -0700
+
+    altos: Increase STM SPI speed to PCLK/4
+    
+    The pressure sensor seems happy at this speed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0cd682ef0c8cdcf364b7e173ff3a9f84e485c113
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 22:23:52 2012 -0700
+
+    altos: Move MS5607 configuration to ao_pins.h
+    
+    Which SPI port and where chip select is to be found are product specific.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3909fca0a3f918121888a415f9bf9bca99505366
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 22:22:58 2012 -0700
+
+    altos: Add missing parens in ao_ms5607.h
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c5bbfc7244faaae41c7b581644c3c253e9b7f462
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 22:21:46 2012 -0700
+
+    altos: Reduce MS5607 reset delay, increase conversion delay
+    
+    Reset doesn't take very long, while doing a conversion seems to take
+    more than 10ms.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 51aef5d4fc29986353ad887f4a67ed2fe35f8c8e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 22:17:33 2012 -0700
+
+    altos: Make STM clock configuration per-product. Fix 32MHz CPU speed
+    
+    This moves all of the STM clock configuration into ao_pins.h so that
+    each product can configure it separately. While doing this, I
+    discovered that the flash memory mode (64-bit, prefetch, latency 1)
+    wasn't actually getting set, which is why the CPU refused to work at
+    32MHz.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1dcfbb05531767e67df45c2799a2fe533834fb71
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 20:28:19 2012 -0700
+
+    altos: Add beeper driver to STM arch
+    
+    Drives the MegaMetrum beeper
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 67044cca5b463772491d0712d0ce07a8f897a476
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 00:10:43 2012 -0700
+
+    altos: Test I2C code on discovery board.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0a198dfcd1b0defe194feaf301f4586e5573d6e9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 00:10:01 2012 -0700
+
+    altos: ARM -O0 flag appears to generate buggy code
+    
+    Use -Os always, which is annoying...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 597c3df668ccf656d8c014f665c154a03166577c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 00:09:18 2012 -0700
+
+    altos: Make ms5607 driver do something
+    
+    It's loading prom values and converting sensor data, but it's getting
+    the wrong answer at present.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e9362841b1075a2ae59eecc73137b20e700567a8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 00:08:20 2012 -0700
+
+    altos: add STM I2C driver.
+    
+    Not well tested yet...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e027247a9ef82746c26bcb8d0a851a0fa06511de
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 00:06:35 2012 -0700
+
+    altos: Slow STM SPI down a bit so the MS5607 returns values
+    
+    It's still not working right, but at least it returns something other
+    than all zeros...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 059f09dbca4703c25b42389b54c6510331c39485
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 9 00:05:18 2012 -0700
+
+    altos: Allow STM DMA channels to be reserved for one use
+    
+    This allows a single user of a DMA channel to reserve it for use
+    without needing to lock the mutex; this is required for DMA from the
+    ADC to work on megametrum as it wants to start DMA from an interrupt
+    handler, which cannot block on a mutex.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f7cfbbce2b94b5ac0094a0e34e1766fe1ceb12c8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 20:35:17 2012 -0700
+
+    altos: Show temperature in stm-demo
+    
+    Use the ADC to show current temperature
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 98aa481741b8fbc617545beda3d295b53de90716
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 20:33:27 2012 -0700
+
+    altos: Use 384 cycle ADC sample time
+    
+    This is plenty fast, and provides nice stable readings.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0bce68e6a0abc19f49c573331164d13643c9bee5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 20:31:58 2012 -0700
+
+    altos: Add STM temperature sensor calibration data
+    
+    Each stm32l has two-point factory temperature calibration data.
+    Provide access to that for displaying temperature data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a41628c97e90770890cce1d6f580e92c1cad1a7f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 18:55:53 2012 -0700
+
+    altos: Add ADC support to megametrum firmware
+    
+    Measures all MM igniter and battery voltages.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9b9357f48597a034211affb3f18fc6089816456d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 18:55:20 2012 -0700
+
+    altos: Add ADC tests to stm-demo
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b5e9d14b4e3e8f29ad8a7bb9b339890be4bcfa2f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 18:54:32 2012 -0700
+
+    altos: STM SPI is not using interrupts itself
+    
+    All SPI transfers are done via DMA, so the DMA interrupts suffice.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5e41d1e03680af9806c599aad80b9b64dd719f9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 18:53:12 2012 -0700
+
+    altos: Add support for STM ADC
+    
+    DMA-based driver for the STM analog to digital converter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fa96ee3db9b57cc2a88e9edd74bb9efcc228ccf3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 18:44:11 2012 -0700
+
+    altosui: remove debug printf from AltosFlightStats
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e56038b65ba1c6413ba9942be3c092644986f126
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 18:36:36 2012 -0700
+
+    altosui: When computing flight stats, auto-detect boost time
+    
+    Detect when boost actually starts by looking for the last low
+    acceleration value before the recorded boost time. This improves the
+    computation of the boost state data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 20066268d8d1853055d0afe108584db34b425fcb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 18:35:26 2012 -0700
+
+    altosui: Integrate accel speed from very start of eeprom record
+    
+    Don't wait for the flight computer to have detected boost--that's
+    often several samples after boost actually occurred, which can leave a
+    bunch of acceleration out of the speed computation.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d35c4df12a3d8e2dab5d41f8d918ff4237f91e46
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 01:01:33 2012 -0700
+
+    altos: Add ability to change radio frequency in TeleTerra
+    
+    Provide a way to set the frequency locally.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4700f63937786e8f6e9b4882363234a33eb54c46
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 01:01:06 2012 -0700
+
+    altos: Make ao_config_set_radio public
+    
+    Allows other bits of code to reset the radio parameters.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a0a650a2be007b0436bd527d6c18f36eef6fbe2b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 00:25:50 2012 -0700
+
+    altos: Force radio channel to zero when setting frequency
+    
+    Otherwise, the actual radio frequency will include the channel offset,
+    which is not useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c7119405a3dd7128120374a2a001bc98ef523619
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 00:25:26 2012 -0700
+
+    altos: Make ao_freq_to_set reentrant
+    
+    Save a bunch of data space this way.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d0d0d20c20e5eeacbc9a1ec1c93141a2044830fb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 00:24:59 2012 -0700
+
+    altos: Shorten help strings
+    
+    Save a bit of code space
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8405efb6ca68c8216413b94e7acbdf51af00554a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 00:23:55 2012 -0700
+
+    altos: Save memory in ao_config.c
+    
+    Shorten help messages.
+    Stop setting aes_key on products not using aes key.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 41a8383ccd29351f3a88a374f9456d6efb71b9a0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 00:22:41 2012 -0700
+
+    altos: Save memory in ao_cmd.c
+    
+    Shrink a couple of help strings
+    move a variable in ao_cmd from data to pdata.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5f63065a16b65618b39314880c72074f0a8b5550
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 00:22:02 2012 -0700
+
+    altos: Add fake install/uninstall targets for stm-bringup
+    
+    Keep global 'make install' from failing.
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5569e4df50648a3ec131ba5e244da9b67d1a67db
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 7 00:21:03 2012 -0700
+
+    altosui: Radio channel gets set to zero by altos when frequency is set
+    
+    Just remember what the channe is going to get set to.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2db6b0f58811ffc44a468c8fbcacc08d37edd26c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 6 22:40:49 2012 -0700
+
+    altos: Shuffle stm-demo SPI test code around a bit
+    
+    Move a debug printf beyond the chip select boundary to
+    allow for more accurate timing.
+    
+    Send four bytes instead of just one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 89201cdf2062b7319a0da4e266e4d6edba1493f8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 6 22:40:17 2012 -0700
+
+    altos: Declare stm DMA address registers as volatile void *
+    
+    Eliminates a cast when assigning to them.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0bcc23c3be1a20a362fea268901c600f9f0d287a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 6 22:39:45 2012 -0700
+
+    altos: Disable DMA unit when idle
+    
+    Should save a bit of power
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 20926b62a87154a846cb950dc542c737cd54826d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 6 22:39:12 2012 -0700
+
+    altos: Disable STM SPI transceiver when idle
+    
+    Should save a bit of power.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9b12bc445fe482306e4587ad60c6d2daf65a60f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 6 18:07:07 2012 -0700
+
+    altos: Add DMA, SPI and MS5607 drivers
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2a04ac8dff1bfc3efba8c7e4dc9c1a827496dbdf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 2 23:00:29 2012 -0700
+
+    altos: megametrum builds an ELF file, not an IHX file
+    
+    Name it appropriately.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cf1e4d60e1fcd75fa734365a2666ea8930938128
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 2 22:58:54 2012 -0700
+
+    altos: Move SPI functions to architecture-specific location
+    
+    Some architecture specific stuff needs to use core altos code, so
+    create new ao_arch_funcs.h files per architecture that get pulled in
+    at the end of ao.h
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c09d155328bd446bb84ac1068d380cceb884df22
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 2 19:27:53 2012 -0700
+
+    altos: Oops. Set per-USART STM baud rate register instead of usart1
+    
+    Was accidentally always setting usart1 instead of the per-usart
+    register. Didn't work too well for other usarts...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 440226df03a85cd0047d876b57b2a3410bfb2b02
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Mar 31 17:53:25 2012 -0600
+
+    be explicit in a couple places about only using single-cell LiPo batteries
+
+commit 4ca52908c8b3f98a79588981f6878025250f0924
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Mar 31 16:43:39 2012 -0600
+
+    use explicit path not $(HOME) to find ARM toolchain for now
+
+commit 246618baf9b8803e5ae4e650eb46740d1128a010
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Mar 31 16:43:11 2012 -0600
+
+    add config file for gdb use with MegaMetrum via Olimex dongle
+
+commit b89f11139fae7ae722ed78d342a169ed2bf5c948
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 31 12:44:14 2012 -0700
+
+    Build megametrum-v0.1 by default
+
+commit bbf31b8591f89e2a1fcc7dac5f42d730a81473d2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 31 12:43:58 2012 -0700
+
+    altos: Add stub cc1120 driver
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1f2b75518169c4f2da9762de46bf1d9a71a04d17
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 28 21:54:32 2012 -0700
+
+    altos: Move cc1111 DMA engine interface to cc1111/ao_arch.h from ao.h
+    
+    It's hardware specific
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 47a9925f16f6a13b173c49d3873d91fc7cf6d46e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 28 21:53:30 2012 -0700
+
+    altos: Move cc1111 AES driver to cc1111 directory
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93ae5f6ce09fa71ebd34f77a884684b3670c8b44
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Mar 31 13:29:15 2012 -0600
+
+    openocd config file for MegaMetrum
+
+commit b711768da6310a1b06f3b995a280587fed5f26cd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 28 21:45:44 2012 -0700
+
+    Bump version to 1.0.9.4
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0993595035a2813deba6991fa25bc0d475f2e6bb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 28 21:45:02 2012 -0700
+
+    altos: Add ao_freq.c to megametrum build
+    
+    I think this will be needed to compute radio settings on the cc1120
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a157edbe1bf7fffd5a6041f7b1760674addd2229
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 25 13:08:05 2012 -0700
+
+    stm-demo does not use altitude.h
+
+commit c1531fb26461b9f4ec39672bbfaeb70e6f4d1056
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 01:42:32 2012 -0700
+
+    Use -Os for STM apps. Fix altos.ld to matchall .rodata* sections
+    
+    Without .rodata*, it would stick the flash copy of the data on top of
+    any further .rodata* sections. Fortunately, the linker catches that
+    and complains...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f2c110fb4531144f18f62200e4127738c84e87f1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:55:53 2012 -0700
+
+    Get config stuff hooked up for MegaMetrum
+    
+    This stubs out enough stuff to let ao_config link and work
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0bda768c2be5b81bec13f21d4d3bb6a11a8e88fa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:43:47 2012 -0700
+
+    Add preliminary MegaMetrum v0.1 support
+    
+    This turns on an LED, enables the internal R/C clock, hooks USART3 to
+    the GPS chip and USART1 to the console.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c27e211796a64b6bbacfe6a1516e9872fdb0853e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:43:17 2012 -0700
+
+    Use new Makefile.defs in stm-demo
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d7ddfd4e6d75e50ca64a342181f5c52e9f4919af
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:42:13 2012 -0700
+
+    Use 16-bits for STM32 LED mask. Export serial I/O functions
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 606d866153cb639a2400cbedbc45046372ad1b30
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:40:48 2012 -0700
+
+    Allow skytraq to be on non-default serial port
+    
+    Provide ao_gps_getchar, ao_gps_putchar and ao_gps_set_speed hooks to
+    let product specify functions for serial access.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 25184baa36c20e3d661f94e642a33e8aae179d60
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:40:09 2012 -0700
+
+    Allow for more than 8 LEDs
+    
+    Provide hook for architecture-specific LED mask (AO_LED_TYPE)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 87ca5c9c5f9cea1b9e14468e4694ce6acc21955a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:19:06 2012 -0700
+
+    Clean up STM build by moving common defs to Makefile.defs
+    
+    Shortens default Makefile a bit
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 41a3fded116a3101789df44647d0eb06be07a25b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:04:47 2012 -0700
+
+    Make stm-demo display a scrolling message
+    
+    Instead of trying to frob the LEDs, which are now owned by the serial
+    port.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9b5e98a3407b369803109bfc1409e4f8b6e848ba
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:04:21 2012 -0700
+
+    Flush LCD changes each time the text is updated
+    
+    Rather than requiring the caller to do it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6337b5f522be11926a6490d7bb27a4f7795da569
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:03:29 2012 -0700
+
+    Automatically set ALTERNATE pin mode when setting alternate function
+    
+    When selecting an alternate function, set the pin to alternate mode as
+    well; there's no sense requiring two separate calls everywhere.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c9e61a4f1f0ce5e5177a2252e8b7a02a578e77f1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:02:38 2012 -0700
+
+    Allow any STM usart to be used for stdio
+    
+    This also adds the alternate pin configurations for the other two
+    usarts
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf060b3e3ed655bbb8464f342d0a0b1745a06173
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 23 00:01:50 2012 -0700
+
+    Allow for more than one serial port in core AltOS
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d236a5c7cd6e9b1d7192e801d63b4bd348cc2f12
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 22 20:02:30 2012 -0700
+
+    Move LCD string output code to ao_lcd_font.c
+    
+    It's all very specific to the 14-segment display, so
+    stick it there.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6da2f5846f2d28ea1f09f60ef2cc3f68113ac62a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 22 19:43:29 2012 -0700
+
+    Add LCD device driver to STM32L port
+    
+    This enables the 6-digit 14-character display on the STM32L discovery
+    board and provides an ascii output to it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a953ac32033ded18adf0cb3ca20134385fcd0a6d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 22 19:42:18 2012 -0700
+
+    Add defines for LCD controller and RTC clocking
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f6e557bc2f0bd6d4272ed00dd09554d27a83be89
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 22 19:41:23 2012 -0700
+
+    Actually enable usarts with the new usart code
+    
+    And move USART1 to PB6/PB7 to avoid conflicting with
+    the LCD pins.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 03fbc18ea17a9e77a1d4d8e0ddb97abbe5da3658
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 21 16:24:06 2012 -0700
+
+    Clean up multiple serial port support for STM32L
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f560c961ae4fedec0c9f11d5b3635fcb0d67ed8e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 21 16:18:26 2012 -0700
+
+    Add support for multiple serial ports on STM32L
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit faf2bea4fdb3dca7fbed35423a4fe4459ee92ceb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 19 11:25:40 2012 -0700
+
+    Save/restore PRIMASK register on Cortex-M3
+    
+    This preserves the interrupt-enabled bit across context switches
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6a14bee42a8431c909bcd74b0acec57329bc0284
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 19 11:24:43 2012 -0700
+
+    Don't disable interrupts before saving interrupt flag on AVR
+    
+    This ignores the interrupt disabled state, so we'd always leave
+    ao_yield with interrupts disabled.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7662a0096a860cddac413a310305fe842830c3b5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 18 22:29:51 2012 -0700
+
+    Add .gitignore file to stm-bringup
+    
+    Ignore generated binaries
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4be74ef0d13114707fd8217907a5ec457f886160
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 18 22:26:43 2012 -0700
+
+    Fix STM LED driver and blink LEDs on discovery board
+    
+    This adds a task to blink the LEDs, after first fixing up the LED
+    output code to enable the GPIO and talk to the right pins for the
+    discovery board.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ab6ea9043b592c25948a70b6204d613756a9a250
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 18 22:10:02 2012 -0700
+
+    Basic OS running on STM32L
+    
+    This gets stm-demo working
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0cc01d378ae96325e429ad608b953661582939b0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 18 22:09:20 2012 -0700
+
+    Add AO_PANIC_STACK
+    
+    Provide a panic code for stack overflow.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1ca46760cf903860dccb4864578558a1abb6e0fa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 18 22:08:48 2012 -0700
+
+    When debugging, send ao_panic message to ao_debug_out
+    
+    Use the low-level debug hooks to get the panic state sent to the
+    serial port.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4bfce11873f34af9621c60f83a8355f85769f6d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 18 22:07:07 2012 -0700
+
+    When debugging, send pre-init output to ao_debug_out
+    
+    Before tasking is running, send output to ao_debug_out to help debug
+    system initialization.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93481fd3d9305a107b88c8a64de4194a6d94dc0e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 18 22:05:46 2012 -0700
+
+    Incorrect type in ao_task_info for wchan
+    
+    Would truncate 32-bit pointers on arm.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8ba5344514f8ed51f6fd69ca09f6c7035c4fd0da
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 17 00:06:59 2012 -0700
+
+    Add stm-demo program
+    
+    This runs AltOS and talks over the serial port.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1d1b24bb20dec09145fbaa6fe6897898d47dd16e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 16 20:24:29 2012 -0700
+
+    These were moved to the src/stm directory
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b8100ea5042013bb4eed67907b4e9d4e5c196df2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 16 20:23:31 2012 -0700
+
+    Add stm bringup timers and move most of the setup code to the stm dir
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9279fd42793123784ce83ca151df6f4630487722
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 16 20:21:09 2012 -0700
+
+    Add STM platform and stm-bringup demo program
+    
+    The stm-bringup doesn't run altos, it just initializes the device and
+    writes stuff over a serial port. Works on the STM32L Discovery board
+    at least, should do stuff on other boards too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e2f13aa43ba79becbff6c9bfc18c665a58d96185
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 16 14:25:37 2012 -0700
+
+    Add example STM32L programs
+    
+    This loads to flash and sends data over the serial link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3c7d1f6cfffb43299041f2850a48177f4e9b83a5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Feb 22 23:26:03 2012 +1300
+
+    'stdin' is a special name in some compiler environments, don't use it.
+    
+    Switch to 'in' in the prototype for altos_serial_set_stdin.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bdfcc9112c41c494de23594963980a730b625cc4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 28 00:49:29 2012 -0700
+
+    altosui: Disable launch controller button
+    
+    The launch controller should just be a separate program.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cf1e95810559584705d0b8a787375938c68e07c6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 28 00:37:52 2012 -0700
+
+    altosui: Add Configure Ground Station dialog (trac #29)
+    
+    Allows the user to configure the teledongle frequency without opening
+    up the flight monitor window, and also shows the teledongle fixed
+    values like radio calibration, serial number and software version.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d60862fce6ac27a97ad6337eea32a4b48645d158
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 28 00:38:37 2012 -0700
+
+    altos: Make sure config values are loaded in 'c s' command
+    
+    ao_config_get may not have been called when ao_config_show is invoked
+    by the user, so make sure the config values are loaded before showing
+    uninitialized data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b98f75dbcccd40c8cbf32c3bfd21bd6f5648b861
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 22:54:17 2012 -0700
+
+    altosui: Sanity check values from device configuration
+    
+    If someone has down-graded and re up-graded the firmware, the config
+    entries may be garbage. Sanity check them to avoid crashing the UI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b6c7ae2c1f8cba7351cd139c49322280d9d3af47
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 22:53:08 2012 -0700
+
+    altos: Reset config version if it's too large as well
+    
+    Any future config values will get trashed if we down grade the
+    firmware, so we must reset the firmware version whenever it gets
+    written.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3f0379db7067eaf104892a82b9c49142087adece
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 22:02:13 2012 -0700
+
+    altosui: Make 'monitor idle' work with older TeleMetrum firmware (trac #28)
+    
+    Older TM firmware did not have the 'done' line at the end of the GPS
+    report, rather it would just stop after showing the Flags value. Check
+    the TM version and stop looking for GPS data when the Flags line appears.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7a9baabaf33db5e30eb4ef8f923a4fd96fd28fb4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 21:49:58 2012 -0700
+
+    altosui: Mark data 'Age' in monitor idle UI too
+    
+    Just like with the flight monitor UI, it's nice to know how old the
+    data in the monitor idle UI is, in case the data link to the TM isn't reliable.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2f19f9a0eaba22789fdc07a52849e8aaf6fe4695
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 21:48:43 2012 -0700
+
+    altosui: Catch attempt to set radio frequency to 0.0 -- use default
+    
+    Monitor idle was setting the frequency to 0, which takes a while with
+    the new native radio frequency setting code. Don't do that, instead
+    pull out the preferred frequency for that, as is done in other places
+    where a frequency of 0.0 is used.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 170510bb183715e9ba580b180f20657d6602644e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 21:13:31 2012 -0700
+
+    altosui: Find actual landing time when computing stats (trac #23)
+    
+    Look for the last time the height went from >10m to <2m from the
+    nominal landing altitude and use that as the landing time when
+    computing things like main descent speed, time under main etc.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0952224c36eba25db34bd147d2d579c66b15bbf8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 18:24:51 2012 -0700
+
+    altosui: Change flight data saving UI to separate download/delete selections
+    
+    First pop up a dialog to select flights for download. Download
+    them. Then, after that, pop up a *new* dialog to select flights for
+    delete. Offer to delete all of the downloaded flights by default. Then
+    delete the flights.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7dd1d62676c1e605fe69a4c0acfe7638c6b79aa5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 18:19:05 2012 -0700
+
+    altos: Optimize ao_freq_to_set a bit
+    
+    Reduces size from 327 bytes to 287 bytes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d77a4ea206d627635159f35c76c744687d4e633b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 11:58:39 2012 -0700
+
+    altosui: Show only supported telemetry version
+    
+    Make it clear in the UI which telemetry versions are supported,
+    providing the combobox only for new firmware which supports all versions.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d8ebb83e64d66fa159e75aa560d39d80bb6d9d04
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 27 10:38:32 2012 -0700
+
+    altosui: Configure radio with new direct frequency setting
+    
+    Instead of computing the radio setting in altosui, let the radio do it directly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c1d12a117b36de7fe8dd992959b890bfd1163e81
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 26 23:35:35 2012 -0700
+
+    Do radio settings solely by frequency
+    
+    Compute the radio setting needed based on the calibration value
+    provided and the requested frequency.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c2550d72aee371676d2f09316051567681e53a7c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 26 22:05:04 2012 -0700
+
+    altosui: Use ConcurrentHashMap for maps
+    
+    This data structure is accessed by multiple threads, so it needs to be
+    re-entrant.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8610fdae8f47e1e8b6e8525227cc912664ecfafd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 26 22:04:13 2012 -0700
+
+    altosui: Show time since last packet in flight status window
+    
+    Makes it easy to see when the UI is wedged, and when telemetry data
+    are being successfully received.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 392a3107b9e9cc8c1ea51df6ff5ec54817adbc65
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 26 20:11:34 2012 -0700
+
+    altosui: Restructure telemetry classes to be more sane
+    
+     * Make AltosTelemetryRecord be a class, rather than an interface.
+    
+     * Inherit from this for AltosTelemetryRecordLegacy and
+       AltosTelemetryRecordRaw.
+    
+     * Remove bogus AltosTelemetryRecordGeneral class.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 77639dae0fb9975219c2e211ea6dd6c7965eeea2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Sep 27 23:50:43 2011 -0700
+
+    altos: Require callsign match in packet code
+    
+    Ignore packets with mismatching callsigns to avoid accidental
+    connections between devices controlled by different stations.
+    
+    As a special case, if the device is configured with the default
+    callsign (N0CALL), then let anyone connect. This allows configuration
+    of new devices without needing to change the ground station callsign.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1b4a4c7b6a0c3f93267f33482f490e7aa25c2158
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 13 10:40:30 2012 -0800
+
+    doc: Add companion SPI message protocol doc
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5d42ded7ebdb0c134c8dac61f41525c37d81ae61
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Jan 13 10:44:23 2012 -0700
+
+    update turnon_telemetrum to v1.2, add more turnon scripts
+
+commit f450f8bc70f857053b26c4379f54a318062e89a1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 3 22:27:59 2012 -0800
+
+    altos: Set correct registers for serial0 baud
+    
+    oops. not going to work very well setting serial 1.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c6821ae734a7efaf2e069f6c5edf9605a9dbe125
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 3 20:46:25 2012 -0800
+
+    altos: flush stdout when fifo from arduino serial port is empty
+    
+    This avoids leaving bytes pending inside altos
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 65b9b5d0d91fef2c7452dc77017f31f176672304
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 3 20:42:41 2012 -0800
+
+    Build teleshield by default
+
+commit 7a42f2e0f145d2d520aed8e241fa39f7cb62b19f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 3 20:41:15 2012 -0800
+
+    altos: Add support for UART0 in async mode
+    
+    And copy bytes from it to stdout.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 085625ff3992454b59583d95a3c415597c51f754
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Dec 30 15:42:28 2011 -0800
+
+    altos: More _asm -> __asm changes
+    
+    Missed a few last time
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 614ab6a991258a5caad0ca12ae35d0288a5b7503
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Dec 30 15:38:32 2011 -0800
+
+    altos: Clean up for SDCC 3.1 - more keywords
+    
+    _asm -> __asm
+    sbit -> __sbit
+    remove variables named 'data'
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bbb94a6a25a106316414a9a281e924f6c56e9f38
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Dec 30 12:27:35 2011 -0800
+
+    altos: SDCC 3.1 wants __at instead of at
+    
+    Older SDCC would accept __at, so just replace all occurances unconditionally.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4783ac9653fd4f816f839452795e0d2e52129d5b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Dec 27 15:47:54 2011 -0800
+
+    altos: Switch teleshield to use radio slave mode
+    
+    And bring it up at startup time by default
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 791c137728dd3398bd7275be13cc9e6be96d7af4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Dec 27 15:37:28 2011 -0800
+
+    altos: Add teleshield directory and bring-up code
+    
+    This is cobbled together from various existing bits, but should at
+    least provide some ability to test a teleshield board.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eff8611e3eb19853b06acfcd7e978c9046cd5f78
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 17 17:05:06 2011 -0800
+
+    altos: Create TeleMetrum v1.2 directory
+    
+    The hardware is software-compatible with v1.1, but it's nice to have
+    the right version number in all of the files.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6baf6f41040f7b074d8cc84ef75e254c5d2b466b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 17 16:58:04 2011 -0800
+
+    altosui: googleearth doesn't accept spaces between coordinates anymore
+    
+    it got pickier for some reason; let's not put spaces in now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dbf7c0c59854e040b65f068069d80716f02fc1bc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 13 23:03:22 2011 -0800
+
+    altos: Fix pad orientation for pre-1.1 boards
+    
+    They use a different code path for accel as they don't have the
+    VCC compensation code in place.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c1e293ff4953b51c19af8b52f2999419ea84e7e5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 19:19:40 2011 -0800
+
+    altos: Add startup and config (view only) pages to TT
+    
+    The config page needs a pile of work before it's useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 02b4e52a5349217df43105eaa1fff6bfc7dac4cc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 19:18:53 2011 -0800
+
+    altos: Enable packet master and debug in TT
+    
+    The code was already getting linked in, so we might as well use it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e7044fd95472449e2fb860cef902fa458b91ccac
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 19:18:23 2011 -0800
+
+    altos: Fix sdcdb settings for telemetrum v1.0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f9e76e0d3492dcc8bf4b5b1f07c3c727cfdc0ef
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:54:14 2011 -0800
+
+    altos: Trim skytraq NMEA messages. Shrink NMEA header matching code
+    
+    Tell the skytraq to not bother sending a bunch of the NMEA messages
+    that we don't parse.
+    
+    Explicitly look for 'G' follows by 'P' instead of having some general
+    header matching code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d3dcb9451f40506abced72783966104645a73bc7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:52:02 2011 -0800
+
+    altos: Allow TT/TBT reports to escape landed state
+    
+    The TM altitude reporting code kept beeping out state and altitude
+    forever, which isn't very useful on TBT or TT.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0f7d7a4fbede63b51208bf051e08aa73dfbf35a0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:51:31 2011 -0800
+
+    altos: Remove LCD debugging code
+    
+    This isn't needed anymore.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 430b439b9a5cc98b32273a233505de2c2f975c59
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:51:03 2011 -0800
+
+    altosui: Add ao_lcd_cursor_on/off
+    
+    This shows or hides a blinking cursor on the LCD.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 75a8490a7bee2a2c7afd559b13f1d9c40c2aa08f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:49:17 2011 -0800
+
+    altos: Add configurable set of channels for TT
+    
+    This adds the channel list for TT, which will be configured by
+    AltosUI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2ba946fea29ff134ffaeaea9d7932f4bd4e953aa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:48:07 2011 -0800
+
+    altos: Export 'ao_cmd_is_white' for use by other code
+    
+    This function tests the current input character.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 20282ef00afe70e3f3193f6f0200254cb2c33e93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:47:08 2011 -0800
+
+    altos: Set default flight log max to 127k on TM v0.1
+    
+    The flash part on v0.1 boards is only 128kB.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5972c642f0de0789e90268bfa19ef8b51c06eebc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:44:36 2011 -0800
+
+    altos: Handle internal and external telem monitoring requests
+    
+    Record separate internal vs external monitoring state, allowing
+    both to happen at the same time, and when either is turned off, the
+    other keeps working.
+    
+    This also adds disable/enable so that other radio users can
+    temporarily take over the radio; monitoring will resume when the other
+    radio user is finished.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ef7f86453d686a49882e8c1b88a59228c4c631a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:37:53 2011 -0800
+
+    altos: Check pdata+xdata memory usage during build
+    
+    The sdcc linker doesn't check the sum of pdata+xdata memory usage, it
+    only ensures that xdata itself is small enough. This doesn't keep
+    xdata below the end of usable ram on the cc1111 though (0xfe000).
+    
+    Fix up the check-stack program to also make sure all of xdata fits in
+    available memory.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2bce71eba9f44b6fcf64e307c8174824c3a0fb57
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:32:49 2011 -0800
+
+    altos: Set SPI fill value each time it is used.
+    
+    This ensures that the final MOSI pin value will be high after a
+    receive is finished.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0a705b62829d492e3a48c81077907cee61afb860
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:31:46 2011 -0800
+
+    altos: Initialize DMA config address at boot time
+    
+    Instead of resetting these each time a transfer is started, just set
+    them once at boot time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4de8bf6da4d725bb0514d032b0708c5cf420e8fa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:30:56 2011 -0800
+
+    altos: debounce buttons
+    
+    Provide API to clear out any button events that happen during startup,
+    and then discard button events 'too close' together.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ad41b5820c2e252627959e4627473f07784be23e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:29:38 2011 -0800
+
+    altos: Build TeleTerra v0.2 by default
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f6db11c3c87725c809c518f5f19b07325faf9c84
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 12 18:10:18 2011 -0800
+
+    altosui: Deal with serial port exceptions a bit better
+    
+    This catches a few exceptions and tries to make sure the serial port
+    is closed afterwards.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b132eefc5f63412bb4a98a4bb72b9055e40d5d42
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 11 22:30:17 2011 -0800
+
+    altos: Make ao_xmem funcs require __xdata void * instead of casting
+    
+    Having an explicit cast in the ao_xmem wrapper macros caused the
+    compiler to generate garbage values for pdata addresses, making the
+    upper byte 0x00 instead of the required 0xf0. Removing the
+    casts from the ao_xmem macros exposed this problem, so a new
+    PDATA_TO_XDATA macros was added, along with a CODE_TO_XDATA macro
+    which serve to cast pointers, with suitable address modifications, so
+    that things work again.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ecde50fbebe68a2e2200a2f8d081fd37074f840
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 11 22:24:22 2011 -0800
+
+    altosui: Make UI Look&Feel configurable
+    
+    Saves the preferred style and uses that for all current and new windows.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 713bd503902526c17a7657c18be947ef8fa6a47a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 11 17:18:08 2011 -0800
+
+    altos: Shrink Skytraq NMEA parsing - common nmea_finish code
+    
+    This shares the checksum testing across all three lines, saving
+    another 103 bytes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d3e8275572550b700563b3bde73cd7a43b608f17
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 11 17:11:39 2011 -0800
+
+    altos: shrink Skytraq NMEA parsing more
+    
+    This cuts another 78 bytes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8266d8d39c0103e68ef3e476b9639574d9a48771
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 11 16:54:18 2011 -0800
+
+    altos: Shrink code size of Skytraq NMEA parser
+    
+    Just mess around with the code to make it smaller. These patches save
+    173 bytes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit df08b2f6de464f4546c1809b931eb4910d88b558
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Oct 27 01:01:11 2011 -0700
+
+    altos: A bunch of missing .gitignore files (mostly)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a7b285ec0945830b8b31877115157ddd12d6a9fb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Oct 27 00:57:45 2011 -0700
+
+    altos: Add i2c driver for avr chip
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0669f0d74fc24c4f1925a45a9975b7a49a65b692
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Oct 27 00:49:23 2011 -0700
+
+    altosui: Only update GPS data when new GPS information arrives
+    
+    Track which telemetry packets are actually producing new GPS
+    information and only update the GPS average position and count of
+    stable GPS reports with new GPS info, instead of on every telemetry packet.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c7e14a2750d437e8b77d68a944a0711e7a0c882b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Oct 27 00:35:35 2011 -0700
+
+    altos: Fix distance/bearing computations. Deal with large values
+    
+    Lots of little math errors dealing with large distances; easily tested
+    as the GPS currently reports lat 24 lon 121, which is a long ways from
+    Portland.
+    
+    Now reports distances in km when large, otherwise in m.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit da330c5975b9f565d059ef8084dfdacc20f34246
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Oct 26 22:49:11 2011 -0700
+
+    altos: Bring up basic TeleTerra v0.2 UI
+    
+    Lots of fun stuff here -- multiple panes of information.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8e4cceedb5b758faae684978299e667c4bb06f4d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 23 15:24:02 2011 -0700
+
+    altos: Flip button IRQ initialization around
+    
+    This matches what the docs suggest
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 90e738a3379d258a48a7c92f6708040fc39c373a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 23 15:20:50 2011 -0700
+
+    altos: Fix battery voltage computation.
+    
+    Full scale is 4.95V, not 3.333V.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f70553106707e3496d07eecb83f0c0a1acad7f77
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 23 14:51:32 2011 -0700
+
+    altos: Add battery voltage sampling driver
+    
+    For devices without a full ADC compliment, this just samples the
+    battery pin and converts to mV.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7e7a10c06a0486e9f869e361e46f2c98db9897b0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 23 14:08:59 2011 -0700
+
+    altos: Add button driver and sample user
+    
+    Hook up the teleterra buttons and have them beep
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8e2736226fcd7c1ab1ba93a5ebac9b285ebf4733
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 23 12:55:35 2011 -0700
+
+    src/teleterra-v0.2: Initialize more hardware
+    
+    Initialize the flash storage and flight state reporting beeper.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 07d4477b2e8477e96a2f155a25f95e14a9a47efa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 23 12:51:05 2011 -0700
+
+    altos/teleterra_0_2: Initialize LCD driver
+    
+    Initialize the LCD for testing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 85b259c5bba7edbd2a79471bb1104bcf3904d536
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 21 14:58:26 2011 -0700
+
+    src/teleballoon-v1.1: Use Tm style initial state stuff
+    
+    Come up in pad mode unless someone talks to us while in idle mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eb61f7aa2c8b692bd892b85e782f249187c80e5c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 21 11:30:43 2011 -0700
+
+    altos: Add teleballoon-v1.1 directory
+    
+    This is an alternate firmware load for telemetrum v1.1.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a10aa835a06b71e2cefeb6b10daaf8cc394603b6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 15 22:54:40 2011 -0700
+
+    altos: Add LEGACY_MONITOR defines to more programs
+    
+    Make all monitoring programs define whether they want all of the old
+    telemetry formats too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aef732fc9cdf527a18f2959d6fb7903e832209da
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 15 22:54:02 2011 -0700
+
+    ao-bringup: Make it build with source restructuring
+    
+    Source code all moved around, need to find the header files in new directories.x
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0debe7ffc2aab2b4d08f42e488cb783ae91c36ab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 23:30:53 2011 -0600
+
+    altos: Add TeleTerra v0.2 product
+    
+    This includes most of the necessary drivers.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 65873a3ad1d8e8b5ec002be2576c6f496543306a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 23:29:24 2011 -0600
+
+    altos: oops -- forgot to add the cc1111 string code
+    
+    This is required for all cc1111 builds now; it provides xdata string
+    functions.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit badda0d910c56135401dce9adc9e6abebdba2ad7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 23:28:02 2011 -0600
+
+    altos: Split out arch-specific bits of LCD driver
+    
+    The arch-specific section just puts a single nibble to the device.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 47c2c0b79dc516d2566ae149605b7d70ef2dca98
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 20:58:04 2011 -0600
+
+    Bump to 1.0.9.3
+    
+    TeleScience and TelePyro now work.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 652c024ed37bfed5de17f45c772796d5cbe4599f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 18:48:55 2011 -0600
+
+    altos/telescience: Add more header dependencies
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 75960500d1f290fa9f82183431443ac122f12c19
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 18:48:11 2011 -0600
+
+    altos/avr: telescience doesn't have room for the flash write code
+    
+    This is useful for debugging the SPI and flash drivers, but not
+    necessary in production code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b80f8ffb61566cbd134c399ea6ccf9290075490b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 18:45:51 2011 -0600
+
+    altos/avr: Must leave space for init stack in ram
+    
+    The stack used during system initialization lives at the top of RAM,
+    so leave some space for that.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f1573a752425121d4c6a14285f1eb0fef3a8bea5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 18:44:48 2011 -0600
+
+    altos/avr: Shrink default stack size to use less ram
+    
+    Not that we have any way of knowing how much stack we're using, but at
+    least this seems to work.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cb837d9bb9e6736fcdfca7692b1f9490ea090838
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 18:13:00 2011 -0600
+
+    altos: Allow ao_science_slave to not log data
+    
+    This is mostly for debugging with flash writes disabled.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ac0bebc44bc657b303db4c41fa0c9624f3df9f4f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 18:11:56 2011 -0600
+
+    altos: Make HAS_STORAGE_DEBUG define consistent
+    
+    This allows products to include the 'w' command for testing flash
+    writing as needed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3a28846d3ff8f82b0e97c211b9debf6d67ee5af5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 18:10:45 2011 -0600
+
+    altos/avr: Clear SPI receive buffer before clocking new data in
+    
+    I don't know why this is necessary, but the receive buffer gets
+    'extra' data added somehow.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e60c470b426b7be08a33133e7d8c94201d7e96d4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 17:44:40 2011 -0600
+
+    altos/avr: Pull-up on the SPI slave select pin
+    
+    This makes the board work even when disconnected from TeleMetrum.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit af4470f8025116179ef83726a8287e47c465907b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 17:44:02 2011 -0600
+
+    altos/avr: Register stdio handler when using serial for stdin
+    
+    This code was left in a bit of a mess; just clean it up.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3bda859caf1501f8408703dca81412d70ba00e04
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 17:42:24 2011 -0600
+
+    altos/avr: telescience does not have a serial port
+    
+    The USART is used for SPI to talk to the flash part.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0a186e92c5773c5d908e9cee889d645a8172dcdc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 17:39:35 2011 -0600
+
+    altos/avr: Make ao_arch_critical argument be a statement
+    
+    Wrap the argument to ao_arch_critical in do { } while (0); to make
+    sure it gets correctly checked as a statement.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3453068b0feb640b9d11dbeb021c535ce8b4a31
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 17:39:06 2011 -0600
+
+    altos/avr: Print newline after dumping ADC values
+    
+    Cleans up the formating a bit.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 06b044629951b06c7ec9b0105b89f51b2880ebd0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 11 16:03:04 2011 -0600
+
+    altos/avr: SPI mutex is now held by the caller, not the SPI driver
+    
+    SPI transactions generally require a read followed by a write, with
+    the chip select held the whole time. As a result, the SPI bus must be
+    held across multiple transactions. To make this reliable, the caller
+    must hold the SPI mutex, instead of the underlying SPI driver.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5d1361c95f94125cda244b4cc5e55c2fb77b680b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 9 11:09:25 2011 -0600
+
+    altosui: Deal with missing state transitions in FlightStats window
+    
+    Any missing start/end times are pinned to the end of the flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5c82b07471f017171c58a6968adf79901f46a987
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 9 10:55:04 2011 -0600
+
+    altosui: Deal with telem data that goes backwards in time
+    
+    The new telemetry stuff can send packets with older timestamps, so
+    sort telem packets read from disk to create an in-order record of the flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 636b7b368e67346b0796cd84fbfd71e10966d61f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Oct 9 10:21:56 2011 -0600
+
+    altos: Respond to telemetry rate changes immediately
+    
+    Instead of waiting for the previous telemetry interval to expire,
+    immediately switch to the new telemetry rate. This will provide
+    more telemetry data early in the boost.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca036c5616c3e745c0b878ed90618d4ff710c0e5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 8 22:19:52 2011 -0600
+
+    altos: Improve TM v1.0 apogee estimate
+    
+    v1.0 boards have noisy accelerometer data caused by interactions
+    between RF transmission and the accelerometer measurements; this noise
+    generates a negative bias in the accelerometer readings. The net
+    effect is that the estimated speed is lower than the actual speed,
+    causing early an apogee estimate.
+    
+    By increasing the sigma value for accelerometer data, the kalman
+    filter 'trusts' the acceleration data less, putting more weight on the
+    barometer data. This causes the estimated time of apogee to be closer
+    to the correct value.
+    
+    This reduces the response to changes in acceleration.
+    
+    This new value is applied solely to TeleMetrum v1.0 boards. v1.1
+    boards correct for this error, and hence can use the correct sigma
+    value for the accelerometer.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 26d7eb7149da8c797d7e704d75f73af2d2aa52c2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 8 21:31:34 2011 -0600
+
+    Bump to 1.0.9.2
+    
+    (1.1 RC 2). Flown at Oktoberfest 2011 on 10/09/2011 (we hope)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aad03e3cab4c328e53d3df47b6dac1d3f7a49229
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 8 16:07:57 2011 -0600
+
+    altosui: Remove igniter voltages from chart.
+    
+    These are just annoying; when we add the ability to turn stuff on/off
+    on the fly, we can add them to the list of available items.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6a7363b3ba99310bd44c9b66f6f5159e46762be4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 8 11:51:05 2011 -0600
+
+    altos: Ignore ejection bumps when doing boost re-detect
+    
+    An ejection charge looks an awful lot like an extra (really small)
+    motor burn. Ignore them by averaging the acceleration during
+    fast/coast using a /64 exponential decay filter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 82604193ed0c522c1fba0072b504fe88b027f1ee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 8 11:50:24 2011 -0600
+
+    ao-telem: Add new program to convert telem data to ascii
+    
+    This reads telem files and displays them in ascii form. It's not done,
+    and it's not documented, but it's a start.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cbf5a649c8b7101bef9d57e48e42ac775e758c79
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 8 11:46:38 2011 -0600
+
+    altosui: Allow for multiple instances of each state in the graph
+    
+    With the new boost re-detect code, we can get multiple instances of
+    boost/fast/coast, so make sure each are displayed in the graph.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f9b0b7423c0640f729d61a91de6ff96ffe4b486e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 8 11:43:37 2011 -0600
+
+    altosui: Max acceleration across boost instead of all ascent
+    
+    This ignores ejection bumps, making the max acceleration far more useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 258b75498916183ed250d3abb3282fe3d843e7a1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 7 09:53:09 2011 -0600
+
+    altos: Write xdata versions of memory functions
+    
+    These are significantly smaller than the general pointer versions from
+    libc on the cc1111.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+    
+    Conflicts:
+    
+       src/Makefile.proto
+       src/cc1111/ao_adc.c
+       src/cc1111/ao_packet_master.c
+       src/core/ao.h
+    
+    Fix up the new makefiles
+
+commit 128bbfa150f88c09f7adde2434b7bf0b5a9ed556
+Merge: f6f54d7 246864b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 7 08:41:56 2011 -0600
+
+    Merge remote-tracking branch 'origin/simple-quiet' into multiarch
+    
+    Conflicts:
+       configure.ac
+    
+    fix version number
+
+commit f6f54d70b768dca1715ddddea64a4df00d82b09e
+Merge: 1c344b7 0d10e25
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 7 08:40:14 2011 -0600
+
+    Merge remote-tracking branch 'uniarch/master' into multiarch
+    
+    Conflicts:
+       src/core/ao_cmd.c
+    
+    Use ao_arch_reboot after waiting for a second
+
+commit 0d10e25766b96f5660e213115cf27b71ff164405
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Sep 27 21:06:54 2011 -0700
+
+    altos: TM: Don't turn on packet slave mode until idle/invalid state
+    
+    Leave the packet link disabled until we've checked the
+    accelerometer. That way, we cannot accidentally get to idle mode when
+    the rocket is on the rail.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fba1d605a627d03f9587ec060c45fb5d3e96aaeb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 26 11:50:28 2011 -0700
+
+    altos: Delay reboot by a second to avoid re-entering idle mode
+    
+    TM and Tm go into idle mode if they receive a packet after boot
+    time. When tebooting the device over the packet link, the packet
+    master would be (rapidly) polling the device for additional data and
+    so the device would invariably receive a packet during bootup and go
+    into idle mode again.
+    
+    Delay the reboot by a second to give the controller time to disable
+    the master end of the packet link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1c344b760776cd5d8c0297d8db9bf02687381b4e
+Merge: 4ed53ef fc4173f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 7 08:34:59 2011 -0600
+
+    Merge remote-tracking branch 'origin/master' into multiarch
+    
+    Conflicts:
+       configure.ac
+    
+    Fix version number and location of ao.h header
+
+commit fc4173ff882dd9718f34ed043276ef612783dfe0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Sep 27 00:59:08 2011 -0600
+
+    add run-time dependency on libjfreechart-java
+
+commit 989aae5b18856e3420ea5b7a26ddd8dccae9d6d3
+Merge: 0552fbe e44f1ff
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Sep 24 15:34:59 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 0552fbed34c9698dac30c239df2a823a8502b3f3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 30 16:59:53 2011 -0600
+
+    include 1.0.1 release notes in docs, closes: #642705
+
+commit 4ed53ef838afd4e922deb1c721a311974527525a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 23 10:24:33 2011 -0700
+
+    altos: Debug code for telepyro doesn't fit in telescience
+    
+    Just too many strings.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f1328d22aab4378c4fb6f0c24dbee95948ca836c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 23:21:04 2011 -0700
+
+    Dump test conversion of ADC0
+
+commit 9b498a6b1327f543c73145e02ff16e76d09f2fe4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 22:54:56 2011 -0700
+
+    Dump ADC registers
+
+commit 7bc007ed45af8fe9ef5daeb7844f183cd9a49035
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 18:13:55 2011 -0700
+
+    altos: Fix make-kalman to run under dash
+    
+    Dash can't deal with >&, so use the old-school > file 2>&1
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 74d5dea5d5ef91db823018b631613d15c6da085d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Sep 21 16:42:52 2011 -0600
+
+    fix bashism that prevents building with /bin/sh->/bin/dash
+
+commit f68f22f527104cdffa1f5e398a51a466a13ff1fb
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Sep 21 16:42:22 2011 -0600
+
+    add missing sense_h entry in TelePyro table
+
+commit 16aa67fc77b82a9d051f205037b27a5384e3e3b7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 14:24:30 2011 -0700
+
+    altos: missing ao_log_single.c
+    
+    Neglected to add this file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7a02d4bace05cc829522933b9df6b82a9e17336f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 11:30:43 2011 -0700
+
+    altos: Add TelePyro v0.1 support
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0820f5c6dfe067590f36e8201a4049719dcf3d7c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 11:40:56 2011 -0700
+
+    altos: Support staging by going back to boost as needed
+    
+    Detect additional motor burns by looking for high positive
+    acceleration in coast or fast modes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c8a00bb9ccdf0d4257f037c2bf996ce5e6b0b0c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 11:39:24 2011 -0700
+
+    altos: Pre-compute RDF packet len at compile time
+    
+    Instead of computing the RDF packet len at run time, which takes a
+    pile of code space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit abf17522c206b465375b73a004a6d67bfa714ba3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 21 11:36:11 2011 -0700
+
+    altos: SPI slave code is now per-product
+    
+    We can't write general purpose SPI slave code as we must eliminate
+    any latency because the SPI ports have no buffering.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 246864b0597ddd397aba39be1fe39df0df189433
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 16 18:27:56 2011 -0700
+
+    Update to version 1.0.2
+    
+    Bdale is planning to fly this version on 9/17.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eaa7d7c7dd445ade7d8e3228306785f9a545b89a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 4 08:49:16 2011 -0700
+
+    altos: Silence radio while firing igniters
+    
+    Hold the radio mutex while firing igniters to reduce potential
+    interactions between the two.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d1e81db87ce0f89fd0b7651ca00979498cd00b78
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 4 08:48:05 2011 -0700
+
+    altos: Delay restart of RDF at apogee
+    
+    Instead of immediately trying to send RDF at apogee, start it after
+    the usual RDF interval to avoid doing RDF while firing the apogee igniter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e44f1ffb7104d70f5c9b9a90529ddbe1b75da074
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 30 16:59:53 2011 -0600
+
+    include 1.0.1 release notes in docs
+
+commit b2d4e49bfe88aa61ca36fb1af8f3088c5754304c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 29 14:22:19 2011 -0700
+
+    altos: Split avr clock initialization to ao_clock.c
+    
+    This will be shared with the attiny code for TeleTerra v0.1
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e66fd72664aae7c000dce9c528803e28e7918fdf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 17:03:26 2011 -0700
+
+    altos: don't beep igniter continuity for telenano
+    
+    Without any igniters, it's not nice to annoy people.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 122c4101164d598e655fa9ad8473053d30ff4212
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 16:55:55 2011 -0700
+
+    altos: Report continuity in telebt
+    
+    Report continuity values from remote altimeter
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9b9568a637ffdbc67225271005f2b996ee08a0df
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 16:39:41 2011 -0700
+
+    altos: add 'report' to telebt
+    
+    This beeps out flight state changes and max altitude at landing, just
+    like the altimeter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 31e3255b6cbfaf95c0e97e2d1ec8de72f845994c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:50:30 2011 -0700
+
+    altosui: Report error message back from libaltos
+    
+    This includes changing all of the error dialogs to show the error
+    message rather than just the file name.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cf72c2f5a69a736c28a9b63e124d510ef41a9f5d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:50:01 2011 -0700
+
+    altosui: Add bluetooth bits back in
+    
+    Stub out functions on mac/windows for now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3d478a39f2ede7b805bbe568cc1c8ecc176d7a04
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:45:05 2011 -0700
+
+    altos: Add the BT serial debug code back in, disabled
+    
+    This code is useful whenever the BT stuff is acting up, so just leave
+    the source in place, turned off by default.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8eaa1c4697a3cfc2406e1adadc3094f7f712341a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:43:32 2011 -0700
+
+    altos: Add pragma to eliminate unreachable code warning on SDCC
+    
+    This pragma was removed as GCC doesn't support it; make it conditional
+    on SDCC so that we eliminate a warning message.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7c6a3195dec6ac68f5d7b3f883ccc2c316384e76
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:42:25 2011 -0700
+
+    altos: Blink on telem packet receive instead of toggle
+    
+    Toggling the LED was too confusing; just blink for 100ms each time a
+    packet comes in.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cff737c290347b61ba16584880c2f4c436b95042
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:40:40 2011 -0700
+
+    altos: Remove RSSI reporting from telebt
+    
+    Telebt uses one LED for bluetooth connection and the other for telem
+    packet reception leaving none for RSSI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 27835686648e14b030f6f7ec1fc0c0fd1c387ea4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:38:12 2011 -0700
+
+    altos: Add RSSI blinking to new-style telemetry code
+    
+    Pull the RSSI data out locally and set the RSSI led blinking when
+    using the variable-length TELEM output mechanism.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6f231a3e512ff7fdd87a399a72c7c36f283394f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:20:41 2011 -0700
+
+    Bump version to 1.0.9.0
+    
+    Make it distinct from any production version
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e53557373e539d591a03d02db146b27d08c7eba3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 28 15:18:29 2011 -0700
+
+    altos: Start logging telemetry data right at boot time
+    
+    Anything logging telemetry data should just start logging packets as
+    soon as they boot.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5c3a0263d292cb0675f608d0ef085d13e51ce6ba
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Aug 28 13:39:37 2011 -0600
+
+    update changelogs for Debian build
+
+commit b33a92e372327158ab21c1bb2d091c58761efe10
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 15:19:43 2011 -0700
+
+    altos: Share log code between telescience and telebt. Add telebt log
+    
+    Telescience and telebt both log data in 32-byte chunks, so share some
+    code which manages that between the two products. Add simple telemetry
+    logging to telebt.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7e2b5e2957ddcb808723081ca7e046a28b7e70e5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 13:30:34 2011 -0700
+
+    altosui: launch controller button needs to move over
+    
+    monitor idle was inserted into position 2,2
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bc827699be4ed9ac60a7e862e6532791968bf685
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 13:28:01 2011 -0700
+
+    altos: Fix up telelaunch Makefile
+    
+    Adding all of the mising sources
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8a1cbef0e316e38c80b71d3bac15641fe56e0a99
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 13:27:28 2011 -0700
+
+    altos: Add HAS_LOG for products that log to eeprom
+    
+    Some products have eeprom for config, but none for logging (like telelaunch)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6afbc1876cd63f64e8975e300692f95a43866f5f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 13:27:00 2011 -0700
+
+    altos: ao_launch belongs in cc1111
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9adf2c9c40ea1da2637ed809dc0d004e47844440
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 13:25:23 2011 -0700
+
+    altos: add ao_aes/radio_cmac to tidongle, teledongle and telebt
+    
+    All of these can do the telelco stuff at this point.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b3c95582774355c991d0a9f27d0a86881e643e62
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 13:24:41 2011 -0700
+
+    altos: move igniter defines back to ao_pins.h
+    
+    so that ao_launch gets them too
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6b2db651a1dbc7ea97fce802b5f10b88be1ab42b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 27 12:42:10 2011 -0700
+
+    altos: Add makefile for telelaunch
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 02df2141e5a67afc16acd01a6c60f3cc61052b93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 1 22:45:43 2011 -0700
+
+    altosui: Hook up the launch controller UI from the main button box
+    
+    Provide a button to start the launch controller UI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4568bc796a6c362ebf7f72ee9a5fa4a9a3c4ba6a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 1 17:08:24 2011 -0700
+
+    altosui: Add primitive UI for TeleLaunch
+    
+    Display status along with arm and fire buttons.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit be117376179126824439d98379079025ca0b245a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 1 17:06:49 2011 -0700
+
+    altos: Keep relay closed while firing launcher
+    
+    Instead of opening the relay while checking the desired state, keep it
+    closed until the state goes off.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bc1b94df4d6b92e794ec93d9c1682ae5f61efa61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 21 22:25:45 2011 -0700
+
+    altos: Finish up primitive telelaunch protocol
+    
+    This adds two commands to teledongle, one to query and one to actually
+    fire the igniter. These will (eventually) want to be replaced with
+    something nicer.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4299b5a36a2f6f9f7bbbc3a1b935dd2357c1fb0f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 21 20:04:05 2011 -0700
+
+    altos: Implement remote launch protocol
+    
+    Uses the radio_cmac module to provide secure communication.
+    Keeps igniter closed for 500ms.
+    Provides remote status for arming and ignition.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 776df9ce2e7b4fa5cedda326988e66c614299af4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 20 23:46:04 2011 -0700
+
+    altos: Get AES CBC-MAC packet transfers running
+    
+    This just has simple command-line based packet transfers for testing.
+    
+    This also adds special ao_telelaunch bits where the launch control
+    code will live.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bd0bf00081bb24af5cd67a9351b0b0c1a041d0d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 14:25:01 2011 -0700
+
+    altos: More work on AES bits
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a731d240f802d37524ce84c3c6acf22bcda4d522
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jul 15 18:51:33 2011 -0700
+
+    altos: Start work on AES and raw radio interfaces.
+    
+    We probably don't want this raw radio interface, but it's a start.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f145be3fc4ee94fdb5c1e2406b6c11d38bdbbd9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 13 20:01:47 2011 -0700
+
+    altos: Start telelaunch product
+    
+    Looks a lot like TM, but without the flight code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 859ee0268b9f2e1f5933019f1231d857a1cac4da
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 13 19:59:58 2011 -0700
+
+    altosui: Add USB IDs for telelaunch and telelco to java bits
+    
+    telelaunch is 0x000f
+    telelco is 0x0010
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b10fbbf0830053a39e4640a53598b1c027615c63
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 1 22:44:13 2011 -0700
+
+    altos: Add 'send all baro' compile-time option
+    
+    This option creates a new packet type that delivers full sensor-rate
+    barometer telemetry data to allow for off-line analysis of flight
+    algorithms using all of the data, rather than the slower rate provided
+    either over telemetry or stored in the eeprom file.
+    
+    Define AO_SEND_ALL_BARO and this will get built in. Perhaps this could
+    be a run-time option...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1c46c419704f661064d200432eb7efeeb11b3859
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 26 20:46:47 2011 -0700
+
+    altos: re-order ao_task to match single-arch code
+    
+    This is just for testing to make the new build match the old build
+    exactly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c32893ce79835a8f861d6ef414644c2ff9769ff6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 26 15:02:43 2011 -0700
+
+    altos: Integrate telescience support
+    
+    Adds a few drivers including an LCD driver
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93b8f40bb451c9ec152490d1f431ab18f8ecb7d1
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 10:52:55 2011 -0600
+
+    more release process doc updates
+
+commit 9451ae5e4ea6ecfa512ba93197351682d4d043dc
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 10:51:18 2011 -0600
+
+    update release process docs
+
+commit 01aee9ebe517ed657692e0a39a31ae0ddfb5b8b0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 10:39:45 2011 -0600
+
+    update changelogs for Debian build
+
+commit bc399d97a8424a5262f66a03e3bc403f793bb337
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 10:38:44 2011 -0600
+
+    roll back packaging changelog for rebuild
+
+commit 13e6e799070a1469cbc2ff990379ee520b8f0e6a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 10:29:58 2011 -0600
+
+    roll release notes version from 1.0 to 1.0.1
+
+commit 1d15c841ad276127edae0345bd316c30731fffbf
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 10:24:35 2011 -0600
+
+    ignore generated log file
+
+commit 58a74c3c3dcd01509224326cffeb67a3e809e4e6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 10:07:15 2011 -0600
+
+    fix telemini firmware path name
+
+commit 0a92eb2fa6b213533691288d8f99d72b80312983
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 09:43:06 2011 -0600
+
+    update changelogs for Debian build
+
+commit 3cea033ec928c21b9f7b810898ed3c3cc536d2ce
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 09:41:46 2011 -0600
+
+    get ready for a 1.0.1 release
+
+commit 674231773256bacd7acb4b5718c47412e47b813f
+Merge: 08e3d54 3bfe8df
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 09:37:01 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 08e3d54bacf8b38a7c33e420a0bc19bcc7acedd4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 26 09:35:54 2011 -0600
+
+    moving git-buildpackage config into .git/ since it is fairly specific to
+    Bdale's build environment, and doesn't need to be in the source package
+
+commit 8125acc030574afed6f23aa8aa302d9c768bb04e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 25 22:55:33 2011 -0700
+
+    altos: get avr-demo to build. Pull in AVR drivers and LCD driver
+    
+    This completes the basic task of making an AVR version of altos by
+    getting the Teensy 'avr-demo' program to build.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e9fab7dc99a0e7c22b511c5919adf7df85213252
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 25 22:04:36 2011 -0700
+
+    altos: add GCC/SDCC compat macros, init_stack, save_context and GCC stdio hooks
+    
+    More arch-indepdency bits.
+    
+    GCC stdio is different from SDCC, so create suitable code in
+    avr/ao_avr_stdio.c
+    
+    Create macros to initialize the task stack frame and save the task
+    context.
+    
+    Add GCC/SDCC type definition compatibility macros
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a588092a7d76dab92e4ab11e0fdb457d2ddc9025
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 25 21:10:56 2011 -0700
+
+    altos: AVR changes - create ao_arch.h files, define ao_arch_reboot
+    
+    Start pulling changes needed for avr port; architecture-specific code
+    will live in <architcture>/ao_arch.h. This first change defines
+    the ao_arch_reboot macro to reboot the microcontroller.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9513be7f9d3d0b0ec29f6487fa9dc8f1ac24d0de
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 25 20:43:44 2011 -0700
+
+    altos: Restructure altos build to prepare for multi-arch support
+    
+    Split out sources into separate directories:
+    
+       core:           architecture and product independent bits
+       cc1111:         cc1111-specific code
+       drivers:        architecture independent drivers
+       product:        product-specific sources and Makefile fragments
+       util:           scripts for building stuff
+    
+    This should have no effect on the built products, but testing is encouraged
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3bfe8df44b575ca430ffaa051e20faa955a06c03
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 25 18:02:45 2011 -0700
+
+    altos: Clear callsign on initial config load
+    
+    Before being written the first time, ao_config will get set to 0xff
+    when the config storage is read. This leaves the tail of the callsign
+    filled with invalid bytes. Zero the whole thing before loading the
+    default callsign to make sure any extra bytes are set correctly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93ecaf7f18f8f4c15953c2e80dc9e1b20d04fdfa
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 15:46:30 2011 -0600
+
+    update TeleMini turnon script now that we've made a stable firmware release
+
+commit c3314dae2d3df82e188daf6ba8520cce833592c6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 03:36:25 2011 -0600
+
+    use multimaint-merge to make Debian changelogs less ugly
+
+commit f9d87de0e2681c2ec5753f4fffef0e61a3a9e144
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 02:22:53 2011 -0600
+
+    update changelogs for Debian build
+
+commit 41998645c04a2ee856dec272b40c2dc5913291d3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 02:16:17 2011 -0600
+
+    add a postinst to remove sources.list.d fragment delivered by old private
+    versions of this package, no longer needed once we have official packages
+
+commit d65e4f6b0a8ba8f67c8a916f2e8be0ec5c75f47d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 02:05:49 2011 -0600
+
+    really, I mean it, no longer deliver the sources.list fragment
+
+commit 0579bd5de9e7686d227a16951dd520439ca5472b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 01:55:32 2011 -0600
+
+    we need a main category in the desktop file
+
+commit 3ec03792adebbeadc9c0d4985c6f51877e8ab969
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 01:48:11 2011 -0600
+
+    update desktop file for consistency with package section
+
+commit 0b1d5b678f0e36fbe09d4d3babffb77f857f098d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 01:37:31 2011 -0600
+
+    rewind packaging changelog to last tagged version
+
+commit f0891fc2e4fa4fd6dd5c2bfc74e4514de986a295
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 01:35:18 2011 -0600
+
+    ao-view is no longer included in the altos package, so have the old-style
+    Debian menu entry point to altosui instead
+
+commit a7ee1049519bf46b3864666999249876009fc3bf
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 01:35:06 2011 -0600
+
+    changes in preparation for upload to Debian
+
+commit 2992184f7a032c95e22023080fbc26443ed786f5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 01:30:36 2011 -0600
+
+    don't deliver sources.list fragment in official Debian packages
+
+commit 3fc92a529dae410a41038cce38c6a44f5e58bd1f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 01:11:47 2011 -0600
+
+    prepare to release
+
+commit 73abe19acf709c00f5352ec12e8cd6edae1d1963
+Merge: 1bd781d 5158493
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 00:34:49 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+    
+    Conflicts:
+       doc/altusmetrum.xsl
+
+commit 1bd781da934c738e0c9294197c7eb622b0710a9a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 25 00:32:47 2011 -0600
+
+    more tweaks
+
+commit 5158493c8df527e7527057c719c75248609eb3dc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 23:21:02 2011 -0700
+
+    doc: Remove duplicate documentation about max flight log
+    
+    This was described in detail in both the System Operation and AltosUI
+    chapters. Remove the duplicate from the AltosUI chapter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e268798dc260311f5f0167909481b41c9d27fc1c
+Merge: 458f816 242344d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 23:06:44 2011 -0700
+
+    Merge remote-tracking branch 'origin/master'
+
+commit 458f816ad23fd6784757b13b244057d4be64260e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 23:06:01 2011 -0700
+
+    doc: Describe max flight log, ignite mode and pad orientation
+    
+    These describe what these configuration parmaeters do, not how to set them.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 242344d3e32e7c7cd9270d708555923fa888e4d8
+Merge: 5c1cf74 94a1b22
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 23:51:38 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+    
+    Conflicts:
+       doc/altusmetrum.xsl
+
+commit 5c1cf7492b82e63a9db9d0238ecbcd2b59486893
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 23:50:31 2011 -0600
+
+    tweak tweak tweak
+
+commit 94a1b220bbfbb64b9772f3ee64a8e9d353d65e94
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 22:29:56 2011 -0700
+
+    doc: Move the remaining command-mode descriptions to the appendix
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit edfb553bb4fa5b0c7c6c658505b2a99d05fb13bf
+Merge: c74ab82 ec96f11
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 23:23:56 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit c74ab82a7b7a6ad6f79129a9ef5954270e7e8f11
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 23:23:45 2011 -0600
+
+    more changes
+
+commit ec96f11666f9cbd98e16caeccd5d399978bde81b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 22:20:29 2011 -0700
+
+    doc: Updating Firmware is now a separate chapter
+    
+    The Flash Image paragraph references it, change the reference wording.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 11099fab63d32f53d0f2e04a7ab04392e39b5963
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 22:18:29 2011 -0700
+
+    doc: Move updating device firmware section to separate chapter
+    
+    This isn't central to operation of the devices, so move it out to a
+    separate chapter
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 50769fbbeaaf61111d363411e0ef0b2868681cf4
+Merge: 425fa99 d92c173
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 23:15:20 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 425fa995aeaccc1ec9ecf011f185b4406df61541
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 23:15:15 2011 -0600
+
+    more tweaking
+
+commit d92c173615a5fb0278ff6878595bed3f8d813e03
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 22:12:39 2011 -0700
+
+    doc: use 'radio link' to refer to packet command mode
+    
+    Make sure 'radio link' doesn't refer to telemetry and eliminate use of
+    'RF link' and other similar but not identical phrases.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 65ca6f0d7c96432413868274b2cfdea4b76683e4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 23:03:23 2011 -0600
+
+    more tweaks
+
+commit 09981cd024297fd4ef093c7468de2b9d5f3c2691
+Merge: a476e76 03c8b27
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 22:42:39 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit a476e76622b6fa70bf7c8883d2a2a64a382fbd78
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 22:42:33 2011 -0600
+
+    more doc tweaks
+
+commit 03c8b2702a45a12c4748cd1ec801d720c816d9e9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 21:39:48 2011 -0700
+
+    doc: Move Packet Command Mode section to System Operations chapter
+    
+    It makes far more sense here.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 221157af586c6fd7368ee858a390f38bc5ed50f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 21:31:05 2011 -0700
+
+    doc: Describe packet command mode a bit better.
+    
+    Include comments about TeleMini in the introduction, and then explain
+    a bit better what the best method of reliably initiating packet
+    command mode are (start operation, then boot telemini).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d4e1aa92b6ce2f3e4c51029595d1d44a7f2f14a0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 22:27:35 2011 -0600
+
+    more doc tweaking
+
+commit 6eff8d5831dde8e690586cd2a97ddf1595cd2674
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 20:59:28 2011 -0700
+
+    doc: Document pad-mode 'on-board data logging' indicator
+    
+    There wasn't any documentation for this field in the 'Launch Pad' tab.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca0879ba6e5295b4fa790705f742eb647a462ea0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 20:42:09 2011 -0700
+
+    doc: Spelling corrections in altusmetrum.xsl
+    
+    Lots of minor spelling errors.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 963649aa064acfe75d2ff4babd9a0d35dc254e86
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 21:39:21 2011 -0600
+
+    doc tweaks through chap 3
+
+commit 3d88e0493ab446d7c7011786390d30618a72d045
+Merge: 02d6545 5a9972d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 21:26:26 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 5a9972d41a87d4204c6c93cacf14e2962cc1c59c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 20:22:29 2011 -0700
+
+    altos/windows: Get latest JRE 1.6 version (Version 6 update 27)
+    
+    No reason to download stale java bits.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cbfbaabb39f9f7709d00cf3dc63cc1bc7563062e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 19:13:03 2011 -0700
+
+    altosui: Make flight monitor font size configurable
+    
+    Tiny netbooks aren't tall enough for the 'usual' font size, so provide
+    a smaller option. Then provide a bigger option, just because.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9849883a754a73b861dd7be530753ff5c2abb499
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 01:48:28 2011 -0700
+
+    altosui: Don't trust companion telemetry record 'channels' count
+    
+    It can be bogus, allowing the code to walk off the end of the
+    allocated data array.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 02d65453225a3807e61b2ac6e2a26da31a05bd45
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 01:53:02 2011 -0600
+
+    update changelogs for Debian build
+
+commit 1d286dab223e6c44a25180f944ccb5ba8ed9d5f5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 01:51:54 2011 -0600
+
+    prepare for another rebuild
+
+commit 82634eae16f9691a6c70a48d0858a399814631f2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 01:50:56 2011 -0600
+
+    add pkg-config to the build deps
+
+commit cf2823cab40aa450375c108bae8ed6b051fbefe5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 01:45:28 2011 -0600
+
+    update changelogs for Debian build
+
+commit bf6f00693c394f885d3378415dc7c8a1815a31ed
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 01:43:59 2011 -0600
+
+    roll changelog back in prep for another test build
+
+commit 06aee245eb74e839e50226fa2ddb967c3977fe7c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 01:41:53 2011 -0600
+
+    another test round
+
+commit 4d94e8f9f807a0bbeab0cdead011e74eeca1d1b6
+Merge: 4b5369d 3b0a9a1
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 24 01:38:58 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 3b0a9a1c87390747492bfef435ac8e0829ec748f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 00:29:36 2011 -0700
+
+    altosui: Try to get dialogs to look a little better
+    
+    grid bag constraints are not my friend.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2165e82327faaada23f0503b8e49b80f938b746c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 24 00:09:51 2011 -0700
+
+    altosui: Add tool-tips to config dialogs
+    
+    Provides more information about the various settings, and, when
+    they're disabled, tells the user why.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 63808e0392f43633f92fee137d968e969dd364c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 23 23:20:00 2011 -0700
+
+    Remove stale tools (ao-dumplog, ao-postflight, ao-view)
+    
+    These tools have all been supplanted by altosui at this point, and
+    keeping them around increases the build dependencies by quite a lot.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 186dfc7c72aa7eba281f29f917088e49e44c2ddc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 23 22:51:57 2011 -0700
+
+    altosui: Use system look&feel
+    
+    Turn on the 'be less ugly bit'
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4b5369dc3da2fc8441eeacbf094537b0cf52240d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 23 21:37:17 2011 -0600
+
+    update changelogs for Debian build
+
+commit 99316a17a9642a207c45610e7cc1c33c2600ece0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 23 21:33:53 2011 -0600
+
+    rewind changelog to 0.9.6.0 for rebuild
+
+commit 192881b7cae49af375cc2b8b7305c937079ee724
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 23 20:11:01 2011 -0600
+
+    update changelogs for Debian build
+
+commit fc7e46b1215ea2b4cc138ed77d8eaa122369b15f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 23 20:10:14 2011 -0600
+
+    turn off auto-tagging during Debian build
+
+commit 998adccc1c4f8f6c44833bbf4a52d9441748b996
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 23 18:56:07 2011 -0700
+
+    altosui: add tool-tips to the button box.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 754b9591574c12ddd6e4ab590c6a5f3806b80213
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 23 13:29:23 2011 -0700
+
+    libaltos: fix Mac OS X function signatures
+    
+    altos_list_start was declared to take a (int time) parameter for some reason.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fa6df3fa21e8f09e70371e6c6cc7827a533b4fe6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 23:35:28 2011 -0700
+
+    altosui: Update mac os X library
+    
+    This has been tested on 32-bit OS X and it works; dunno about
+    64-bit.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 955989147f90a4fd22c1375d1b41425dae4e7dd1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 23:34:55 2011 -0700
+
+    altosui: Reset all config data on 'reset' command
+    
+    This lets you switch altimeters without getting any stale data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d249da3fb064754753bd20cd2ca1e5ffcce294ca
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 23:33:52 2011 -0700
+
+    altosui: Only 'show' config dialog once
+    
+    Otherwise, the dialog jumps back to the initial position each time the
+    data is updated.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e9254c3472e42d93181674b2c3cd80fe6eea696e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 23:32:36 2011 -0700
+
+    altosui: fix 'magic' string to signal end of config data
+    
+    Was using "done", which happens to be displayed by the 'l'
+    command. Switch to 'all finished' which doesn't appear in the config
+    data output. Yes, this method is a kludge.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4aebe65b089e4b825a5ae238b81e2181bd88175a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 23:31:54 2011 -0700
+
+    altosui: Can't configure flight log max on TeleMini
+    
+    It's only got space for one flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3a84e8e0cc86481c301f4335843a0e1a94bad5c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 23:12:30 2011 -0700
+
+    altosui: Make monitor-idle display correct 'On-board data logging' status
+    
+    Count number of stored flights and see if there's space for another
+    one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit afe6aba9cb91e93234ffee2a22eee40f848ddedd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 18:25:34 2011 -0700
+
+    altosui: Add --summary option to dump flight stats to stdout
+    
+    useful for quickly capturing sense of a flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4e2fd7ae76c23aa8da1390ebcbd8f45276cd7a32
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 18:24:54 2011 -0700
+
+    altosui: Show filename in AltosGraph window
+    
+    Makes it easier to tell multiple windows apart
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f4ea46dc205454411c224ada7805f813989efd4a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 18:23:41 2011 -0700
+
+    altosui: Add date/time/serial/flight to flight stats tab
+    
+    And switch to using the AltosConvert units conversions functions.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b4c71ba56c471720c72853057d0a527825a78fa0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 18:22:21 2011 -0700
+
+    altosui: Capture date/time/serial/flight in AltosFlightStats
+    
+    Time is the time when boost was detected.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5ef731bd87c6d2a71a4edcc69a218eaf1aa7c465
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 18:21:35 2011 -0700
+
+    altosui: Add a few simple unit conversions
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b83d8eca433ed5796835f6a09271f50c7f27cc81
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 17:18:02 2011 -0700
+
+    doc: Add Installation Recommendations chapter
+    
+    Document installation suggestions, including mounting, RFI, antenna
+    issues and ground testing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a0f62b8569c5535a2598cfb6ab52db79f0a52f92
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 22 17:17:43 2011 -0700
+
+    doc: Add note about telemetry disable mode to 1.0 release notes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 393d231b9689cd1c358600ee76e0e808f89670c8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 21 22:52:45 2011 -0700
+
+    altosui: Attempt to make both 32- and 64-bit windows DLLs
+    
+    We'll see if they work...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d5bd40847b17c32405dfba864a2a5a3b19aa7e85
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 21 22:12:53 2011 -0700
+
+    altosui/windows: Fix a bunch of windows compiler warnings.
+    
+    Some of these may have actually been serious -- a write length was
+    getting stored in a signed char...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2353d83be15b398754c2564f95374c6ea0f8de92
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 21 22:12:04 2011 -0700
+
+    altos-fat/windows: Check and install Java 1.6 as needed
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a08826292ebd802a1ff2effccac3b96fd061c47d
+Merge: 3366cfe 55be3db
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 22 16:08:55 2011 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 55be3db2e31fe97e7f351e3c490b8bc4cf7192b2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 21 19:18:54 2011 -0700
+
+    altosui: Clean up command line processing. Add --graph
+    
+    Make the command line processing a bit less ad-hoc, track 'mode' of
+    processing and deal with all files on the command line.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6823ad5e48fc0a19791d96f886b5689f88c4311b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 18 18:02:02 2011 -0700
+
+    altos/altosui: Add ability to disable telemetry/rdf completely
+    
+    This turns off the telemetry system so that it never transmits telemetry
+    or RDF tones. In idle mode, it will still accept packet mode connections.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a08e7ac8bd3840b699c9a1ffc6b1e115b2b84bad
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 20 11:35:55 2011 -0700
+
+    Bump version to 0.9.7
+    
+    Bdale is flying 0.9.6 at chili blaster, make sure later firmware gets
+    a later version number.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f2b0900f8b83fcb3085f3d042ffd961ffd758d5d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 20 11:28:53 2011 -0700
+
+    altos: Merge common config code in ao_config.c
+    
+    This shuffles code around in ao_config.c to share some common code
+    segments for starting and committing config changes.
+    
+    This also stops printing out changes as they are made which aren't
+    needed by the UI code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 67f28c58db0deca8f8050d33e97ad96017f4baaa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 20 11:19:57 2011 -0700
+
+    altosui: Disable 'max flight log' config when there are stored flights
+    
+    When flights are stored in flash, the maximum flight log value cannot
+    be changed as the flight data might need to be moved around in
+    memory. Check for this case by looking for stored flights and
+    disabling the combo box when storage is not empty.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fcff63baf8fde1174571a2c7c860099e19dbf629
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 20 10:43:28 2011 -0700
+
+    altosui: remove debug printf in pad pane
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3366cfe6145f64cdb04654854d5390c671a288e3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Aug 20 09:28:04 2011 -0600
+
+    update changelogs for Debian build
+
+commit 787e4305a05e344c018abb872f788749203690d7
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Aug 20 09:24:29 2011 -0600
+
+    update changelogs for Debian build
+
+commit 605627d03eeb9f34026a84134992ad061b5b3946
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 19 22:47:50 2011 -0600
+
+    update changelogs for Debian build
+
+commit 048b3eb45169e572f33c68ff152b89db9ef97d31
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 14 21:11:41 2011 -0700
+
+    altosui: Add 'On-board Data Logging' indicator to pad tab
+    
+    This shows whether the on-board data memory is full, or is ready to
+    record the flight. This is indicated in the telemetry stream by a
+    flight number of '0'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit adb7d345963ab9981c49c7cc68c6b4d7156dce46
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 14 21:09:42 2011 -0700
+
+    altosui: Ancient log files used 'apogee' for 'coast' state
+    
+    2009-07-18-serial-004-flight-000 says 'apogee' for the apogee-detect
+    phase of the flight; map this to coast so that this flight replays
+    correctly (although the log terminates at apogee...)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4f64d66295a8f76680af8cfda4650aa4c4f8576d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 14 19:19:50 2011 -0700
+
+    altos: Lost change that reported flight 0 when log memory was full
+    
+    commit 52ac83fedbfd380d14d4df2e79992bbdfba3552a added coded to check
+    for a full flight log and complain with a special tone.
+    
+    It also reported flight 0 over telemetry, and that part of the patch
+    got lost when moving to the new telemetry format. This patch
+    resurrects that piece.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ef7f60df841f1eb22b9cec0d7f68cf2c003d6b30
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 14 00:00:54 2011 -0700
+
+    altosui: Respect storage limits in flight log max config
+    
+    Compute the maximum flight log using the data returned from the 'f'
+    command (total storage and erase block size). Limit menu to choices
+    which fall within this limit, complain if the user asks for too big a value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 746d6a472a20243a8c0eacc8edf8e81e0641bc17
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 14 00:00:07 2011 -0700
+
+    altosui: don't set channel when using radio setting
+    
+    altos now sets the radio back to channel 0 when the radio setting is
+    changed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 709485f20fb039f8dd087c8491c5f5a76718ae53
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 23:58:03 2011 -0700
+
+    altos: use raw height while waiting for landing
+    
+    This avoids any noise introduced by the kalman filter, making landing
+    detection much more reliable. This patch also changes the interval to
+    10s so that the height bounds can be increased to 4m.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aa71c2cdcb417eba2a2d30792ece9a47b8b3fc82
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 23:56:55 2011 -0700
+
+    altos: Reset radio channel to zero when using radio setting
+    
+    Otherwise, it's hard to set the frequency over the radio link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f74761051f2a5ab45b82c4dd79a8569376bbe2e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 23:56:06 2011 -0700
+
+    altos: Correct AO_CONFIG_MINOR from 6 to 7
+    
+    Forgot to bump this when adding radio setting.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 924d56a4d2d8b16530cd378b18cfc5d6e08420ed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 21:10:15 2011 -0700
+
+    altos: AltosSerial.flush_input shouldn't discard Interrupted exceptions
+    
+    The eeprom download code wants to interrupt serial communication so
+    that it can stop downloading stuff in the middle of a run. Make
+    flush_input pass the exception along instead of discarding it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dcd15032eec45f3fdd003050710ebd5b85052662
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 21:09:19 2011 -0700
+
+    altosui: Eliminate inter-chunk flush_input calls
+    
+    Once the serial line is nicely synchronized, we don't need to flush
+    input between chunks. This speeds up eeprom downloading quite a bit.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3ba7b6196f68078f4ed4538c4e7fe30699dfe908
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 21:05:46 2011 -0700
+
+    altosui: Devices with log-format can also delete flights
+    
+    Any device with either flight-log-max or log-format can delete flights.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 03e201e1acc8742399054e4ad36b533120ea1612
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 18:39:35 2011 -0700
+
+    altosui: Add support for TeleScience eeprom download
+    
+    Using the existing eeprom methods, fetch and save TeleScience eeprom
+    data, storing to a filename generated from the serial/flight from the
+    TM connected to the TS board.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5a3e96bef31959a287b8696778d7d8cf911a7dc4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 18:36:18 2011 -0700
+
+    altosui: Clean up eeprom parsing a bit
+    
+    Export basic parsing and checksum functions for shared use.
+    Create 'erased' function to check a chunk of eeprom data for data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b0ec30de37aa822ba66d25ceaa8cf8dc967b4371
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 18:31:08 2011 -0700
+
+    altos: wait 10s for companion to boot
+    
+    In case the companion is delayed while booting, retry the setup 10
+    times with a 1s delay between tries.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 41e5be32819d305c8268e6f992be91411ea13435
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 18:30:00 2011 -0700
+
+    altos: Send serial/flight to companion board
+    
+    Lets the companion log them for later matching with TeleMetrum log
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fa7dd04741bf3fd9cedc59ed3b45b69ef9312609
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 21:41:25 2011 -0700
+
+    altos: Send SPI message at flight state changes
+    
+    Get the companion board starting its data logging as soon as possible
+    after boost starts.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c7f540330c040c521f9d7626009a406e704a5e41
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 14:58:34 2011 -0700
+
+    altosui: Add companion support to the flight UI and CSV conversion
+    
+    Shows the companion data in a new tab. Also put companion data into
+    CSV file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 18369c58e62bc64b969a7cf8be3103aa33c6d4aa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 06:29:13 2011 -0700
+
+    altos: Check for companion init packet validity was busted
+    
+    Was using board_id == ~board_id instead of board_id ==
+    ~board_id_inverse.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aa642cf55c43188e9a21198d828d7ea90ff54280
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 05:42:05 2011 -0700
+
+    altos: add the 'L' command to show the status of a linked companion board
+    
+    This prints out whether there is a board connected, along with the
+    various values fetched from it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f87f0787fa5aa528674f3f4919eb22646c87c25a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 05:41:15 2011 -0700
+
+    altos: Make sure companion task exits cleanly when done
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ebe2ffb29944abc4d6a35889c7b5f3d9f2871077
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 05:38:16 2011 -0700
+
+    altos: Put SPI in slower mode when talking to companion board
+    
+    The AVR CPU on the other side just can't go very fast. This reduces
+    the SPI clock by a factor of 16, just under 200kHz. As the companion
+    commands are reasonably short, this shouldn't have a huge effect on
+    overall SPI utilization.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 30abbdc7ffcfc809b4a3fc31486fe968161ea225
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 9 16:59:16 2011 -0700
+
+    altos: Add SPI-based companion board support
+    
+    This sends current flight state information and retrieves companion
+    data to include in telemetry.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 578c4b17b8f62f2727654ebda78ee139f9fe13fa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 18:38:38 2011 -0700
+
+    altos: Don't try to use non-basestations for remote eeprom download
+    
+    Companion boards may also have eeprom data to fetch; don't try to use
+    them as a radio.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf06af154e232d4caa1585a1d6d5279a075292e4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 21:43:56 2011 -0700
+
+    altos/altosui: Report log format in the version command
+    
+    This will make it easier to figure out what the contents of the flash
+    should look like from altosui; the current 'guessing' mechanism will
+    not scale to many more formats.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c2f2f519dbc8ce233ab36222088c1be6b1362f01
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 18:28:38 2011 -0700
+
+    altos: re-write a bit of GPS parsing code to reduce size
+    
+    Use a local variable while computing hdop.
+    Place the next incoming character in data instead of pdata.
+    
+    Saved a surprising amount of memory.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7bcf25606cd5892d58295649f3d475d284494ee8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 12 05:26:09 2011 -0700
+
+    altos: shrink text space from ao_config.c
+    
+    Eliminate redundant config initializers by setting minor to zero and
+    letting upgrade code handle all of the new values.
+    
+    Stop computing (fake) frequency when showing radio channel
+    Stop computing feet when showing main deploy height
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 313d740b5284b24f1cc7a1ba5779136b55d49ebe
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 18:33:07 2011 -0700
+
+    libaltos: Mis-allocated device list in libaltos
+    
+    Would overrun mis-allocated array, causing chaos.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3b87dd6f46922cf5f98deb2dffa2148c4244e48e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 13 15:00:14 2011 -0700
+
+    ao-tools: ao-list was crashing with more than 3 devices connected
+    
+    the list of devices was getting realloced for each new device, but
+    that realloc was too small.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 640422c028a2be898aa3a9048a0f6fad2e43dd8d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 11 16:11:34 2011 -0700
+
+    altos: remove monitor disable stubs from altimeter code
+    
+    Monitor mode in the ground-station boards must be disabled when the
+    radio is going to be used for another purpose, or the radio parameters
+    changed. That places monitor-mode disable calls in other parts of the
+    system which are shared with the altimeter code.
+    
+    Elide the ao_set_monitor calls for builds which do not include any
+    monitoring code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1d13460412046c53f36466193329caaa657bb278
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 11 15:59:47 2011 -0700
+
+    altos: Apply igniter boot pulse-width reduction to telemini
+    
+    TeleMini needs the same fix as TeleMetrum to reduce startup igniter
+    pulses.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b520c32bcddabd42c07ceafa827694a3ae23a76f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 11 15:57:58 2011 -0700
+
+    altos: Pull igniter pins low as soon as possible at boot time
+    
+    This reduces the pulse width on the igniter circuit caused by the
+    default cc1111 pin configuration at powerup time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 566b16e67be38c6425e616a5c38d641c4e1a9b12
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 22:43:26 2011 -0700
+
+    doc: Add 1.0 release notes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a07b07d48f71b9a11e73a82db075cc57bad0c09f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 22:14:32 2011 -0700
+
+    doc: Add release notes, include them in altusmetrum doc. Shuffle altusmetrum
+    
+    This adds release notes and includes them in the main altusmetrum doc
+    as well as making stand-alone html available for inclusion in the website.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5aa3e49f794ba5ed2680016f3dca47d67ae99836
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 18:32:05 2011 -0700
+
+    doc: Add telemetry docs to debian/linux/mac/windows packages
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7283deaa91e752acc45018ef2ea2f560b09af354
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 18:22:16 2011 -0700
+
+    doc: Describe 'stats' tab in Graph UI, 'Graph Flight' button.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 21837e0026c87635abf4baf2c6c574a7b274f449
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 18:14:10 2011 -0700
+
+    doc: Document Ignite Mode and Pad Orientation configuration options
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 967c9d5ee691f87bf0d1e49ba055eb366e513e6a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 17:43:58 2011 -0700
+
+    doc: Update altusmetrum.xsl for v1.0 software and TeleMini
+    
+    Add TeleMini references and sections as appropriate, update AltosUI
+    docs to describe new bits.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit be65308182363ca87db69db530297933324f3806
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 16:01:25 2011 -0700
+
+    Bump version to 0.9.6.0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9f5623c8c32a38eaeb63fa74ab370025ac015d52
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 15:00:44 2011 -0700
+
+    altosui: Move launch-sites.txt file to altusmetrum.org
+    
+    The official URL is now:
+    
+       http://www.altusmetrum.org/AltOS/launch-sites.txt
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4962bcf1ce15c21a946ea718bd676b901f0f2bd0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 14:35:21 2011 -0700
+
+    altosui: Plot reasonable data from Tm files
+    
+    Don't plot acceleration based on baro data.
+    Display baro speed if accel speed isn't available.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 01b9352eb8ca0e4e2d023ce973c4e863cdcc0c51
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 14:34:39 2011 -0700
+
+    altosui: Prune telemetry file graphs to just the flight
+    
+    Remove data earlier than 1 second before boost and data after landing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9e1487b1a5db0afd1d23c86d82c60b1c1a62aab0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 14:08:21 2011 -0700
+
+    altosui: Add a 'Graph Flight' button to the 'landed'  tab
+    
+    This lets you see the results of a flight as soon as the rocket lands
+    using the telemetry data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6ac604d11de44cd824f09e4b467264a2b74be7bd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 10 13:35:26 2011 -0700
+
+    Altosui: Add flight statistics tab to graph window
+    
+    Provide basic flight stats alongside the flight graph.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 94d9a2c36fabdf24d6a0b985851e95e4eb181fd9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 9 18:28:19 2011 -0700
+
+    altosui: Ship TeleMini v1.0 firmware with fat blobs
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a680ce61bdcffeacb7f0e4dcef71a03cb7cfe07d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 9 18:27:19 2011 -0700
+
+    altosui: Ensure serial code tracks reply nesting correctly
+    
+    Trap any exceptional return conditions from 'get_reply' to make sure
+    in_reply gets decremented.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 12bfa6cc42e3689f09abae2bd2584cbacf2aa2e0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 9 18:26:07 2011 -0700
+
+    altosui: Don't export product defs from libaltos
+    
+    As we add new products, that would change the ABI generated for java,
+    invaliding old library versions sitting around for windows and mac.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 81ad44d4b6d9ad2f6b91d0906e8543da82da424f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 9 14:26:43 2011 -0700
+
+    altos: Switch telemini from v0.1 to v1.0
+    
+    TeleMini production boards are firmware compatible with the v0.1
+    design, so instead of creating another product, just rename the
+    existing one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3985ef8bc69bcec13ce155567a8ed7c5c6051cb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 20:41:34 2011 -0700
+
+    altosui: Add close button to 'fire' dialog
+    
+    Easier to hit than the tiny close box in the frame.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cbf54a826d12c49b1b1996be247869d5ff4e2236
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 20:38:44 2011 -0700
+
+    altosui: Make set of telemetries to use while scanning configurable
+    
+    with a preference to remember across application runs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7146311d9df541e075b4450cf9656a9aa7ffdd93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 20:38:14 2011 -0700
+
+    altosui: Reading serial from swing thread only bad if remote
+    
+    Make the warning on this condition based on whether the link is remote.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2662c577a895c96fce7b2bf815b9e752d2dfbde6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:53:59 2011 -0700
+
+    altosui: Remove unused AltosConfigData from AltosTelemetryReader
+    
+    Now that AltosSerial manages this data, it's not needed here.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 13eacb49de4312509c3a729a31dcda4d601f8a8b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:53:27 2011 -0700
+
+    altosui: Flush radio setting to serial device
+    
+    When changing frequencies, make sure the device hears about it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97cf285d041062ae473c2823438b81c8fffe7f67
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:53:03 2011 -0700
+
+    altosui: Remove debugging printfs from AltosSerial
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 364102d29ff4de0c252774f26417587fa88b7467
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:52:11 2011 -0700
+
+    altosui: Show AltosFrequency in scan results
+    
+    Include frequency and description instead of just frequency.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d4cc16e111229b02d1081e2693ace0b33f662498
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:51:16 2011 -0700
+
+    altosui: Save frequency after setting it in AltosFlightUI
+    
+    Otherwise we'll just save the old frequency.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 81bb6f42d8b859195ea5a35806c42d98ba82e8e1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:49:45 2011 -0700
+
+    altosui: Have single radio_to_frequency function
+    
+    This takes all three radio params (setting, cal, channel) and computes
+    the current frequency.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c8c01684fa011acf3bbe5c3ebbc84aa8e8457a5e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:47:36 2011 -0700
+
+    altosui: A few misc cleanups.
+    
+    Initialize radio_setting as it won't be set for older devices ever.
+    Remove unused set_radio_frequency function from AltosConfigUI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dd383b86b9a13d7af2d6b07f4fb85ccc666ed898
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 18:45:36 2011 -0700
+
+    altosui: Must set radio calibration before radio setting
+    
+    Setting the radio calibration erases any previous radio setting as
+    the radio calibration change invalidates any previously computed radio
+    setting for a specific frequency.
+    
+    Hence, the radio setting must be configured *after* the radio
+    calibration value lest it be ignored.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 40544dbfe09c64f7764a5f0686415805611fab25
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 17:46:04 2011 -0700
+
+    altos: minor type in comment about accel correction
+    
+    Kurt Roeckx found a typo in the equations describing how the
+    accelerometer is corrected by the 5V reference measurement.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a315b200cd0da1a964f5395cd59660be1b49672b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 12:31:48 2011 -0700
+
+    altosui: Pull out BlueTooth support
+    
+    This leaves the code in place, but commented out so that it isn't used
+    until we've got a bluetooth device ready for use.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a65daf94e8fe3e22f770ef76d9104c3dd11d0330
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 12:25:30 2011 -0700
+
+    altosui: altimeter is not spelled altimter
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 84ba927f503f81543dec286c4881be30bb5e60c5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 02:05:28 2011 -0700
+
+    Set version to 0.9.5.0
+    
+    Make the frequency-based systems distinct
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0e3e4f9c1e6a6bf972514f12c9d622258aa2aec2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 01:47:29 2011 -0700
+
+    altosui: Convert from channels to frequencies
+    
+    Major areas:
+    
+     * Preferences are stored as frequencies instead
+       of channels
+    
+     * Serial configuration is done using frequencies
+    
+     * UI is presented with frequency lists
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f03ca0ab8799bfa5100eaa2577cfd7b9c37d05bf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 7 14:52:29 2011 -0700
+
+    altosui: Add dialogs to configure 'common' frequencies
+    
+    These are stored in preferences, but not yet hooked up to the TM/TD
+    configure dialogs
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ba5dc35388d28c5769eaabc970c4d4b8c2c2ff9c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Aug 3 23:07:53 2011 -0700
+
+    altos: Add ability to set arbitrary radio frequency
+    
+    This adds a separate config parameter to control the raw radio
+    frequency setting, allowing the user to select an arbitrary frequency
+    instead of being forced to choose one of the 10 pre-defined 'channels'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e1e5c9b3e24670e9f58c6f7389eafb3338efdb40
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 12:08:45 2011 -0700
+
+    altos: Remove pad_orientation functions from non-accel devices
+    
+    Anything without an accelerometer can't detect pad orientation.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7207a95823dc2a27906759528dd88256cb20679f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 12:04:00 2011 -0700
+
+    altosui: Change button to 'Configure Altimeter'
+    
+    Now that we've got more than one model.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 30670732ca3f5a34025ab4bc4c69afa45637b4d6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 8 11:58:23 2011 -0700
+
+    altos: Correct flight log max on Tm to 5k
+    
+    Was using the wrong #define name to check for Tm/Tn devices that use
+    internal flash for data storage.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a67c16958df8e60b131b01e00fd5bca590af0e7d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 09:38:53 2011 -0600
+
+    update changelogs for Debian build
+
+commit 4a507898d6de631bb2e8ed4aa3e0933d97222323
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 09:34:59 2011 -0600
+
+    rewind changelog for re-build of 0.9.4.5
+
+commit 5082b4998b8a9787e0e2f4d96d912e434aa9b81a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 09:31:18 2011 -0600
+
+    clean up all existing lintian warnings
+
+commit f26c7172a1b2b5344fae6ede562f2da7a56b80e3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 07:54:14 2011 -0600
+
+    build depend on bluetooth dev package
+
+commit 3cad81b0ebb52352c66643a2587e94a2b693a2d2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 07:38:48 2011 -0600
+
+    stop doing automatic tag push during builds
+
+commit ebaad64525119c0fdacf382adc2c99e5df5be23f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 07:37:23 2011 -0600
+
+    update changelogs for Debian build
+
+commit 6f9f75cbfccf975204ab847ca0e9cf86188716c6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 07:35:47 2011 -0600
+
+    update changelogs for Debian build
+
+commit 7c94945690a6ff5cbf872026fd6411a30089c7b1
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 07:33:09 2011 -0600
+
+    simplify version in last changelog entry so git-dch is less confused
+
+commit 6d5e5b0be237d5448aa9d15decd6127cf41ca7b2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 8 07:30:42 2011 -0600
+
+    update version for a Bdale build for use turning on TeleMini v1.0
+
+commit 6492218fc316f8cf6214a577807a8dd0a80a9b6a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 2 19:07:56 2011 -0700
+
+    altos/altosui: Add pad orientation configure option
+    
+    Allow TeleMetrum to be operated with the antenna pointing downwards on
+    the pad. This provides some additional flexibility when designing an
+    ebay.
+    
+    The accelerometer calibration levels are flipped around to match, so
+    no re-calibration should be required.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 11a2bb8e28df7ed87542f2ee726f877971f5d52a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 2 17:41:53 2011 -0700
+
+    altosui: Add idle monitor dialog
+    
+    This monitors a telemetrum device in idle mode, either directly or through a
+    teledongle, allowing the GPS status and batteries to be monitored
+    without resorting to placing the device in pad mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3cc2eed6cdafe788a8617ab45c6664077e76411e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 2 18:01:52 2011 -0700
+
+    altosui: Simple timeouts don't work with query data
+    
+    To get the query to come back, it's best to abort and retry the
+    command, other wise the command may have been lost to the previous
+    connection.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 37c41c962ea4631e62307a57d2ce6572b87fd743
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 2 17:41:02 2011 -0700
+
+    altosui: Parse accel cal from 'c s' command
+    
+    These fields weren't used before, so the code to parse them hadn't
+    been written.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 95201e7fe4a6a7ec42321e8dbad3aea3bbf4c840
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 2 17:40:02 2011 -0700
+
+    altosui: Standard text field in flight UI needs more width (now 20)
+    
+    Latitude and longitude take more than 16 characters.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 82e04a0e3a3296288a524ec582785a36fd644331
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 2 02:09:23 2011 -0700
+
+    altos: Require sequencing through 'main' state before landing
+    
+    The old version of the code would permit the flight to go straight
+    from 'drogue' to 'landed' without passing through 'main' at all. This
+    meant that a false landing detection would leave the main charge
+    unfired, potentially causing the airframe to land on drogue alone.
+    
+    Requiring that the flight sequence pass through main ensures that the
+    main charge will get fired at the right time, although if the airframe
+    lands higher than that altitude, it will not go to 'landed' mode ever.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6c55bf35b11ae3ddae152795072d69e98184bac1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 2 01:49:35 2011 -0700
+
+    altos: Reduce height averaging filter time constant
+    
+    Using the longer time constant could lead to false landing detection
+    just after apogee, which is definitely not a good idea.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e19a117b99e8374ca0e8e35948e23bc672ad1a32
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 1 22:33:38 2011 -0700
+
+    altos: Average height values for landing detection
+    
+    Instead of using the direct output of the kalman filter and hoping
+    that is quiet enough to detect landing, filter that with a long
+    exponential decay filter and then check to make sure that doesn't
+    change more than 2m in 5 seconds as a trigger for landing detection.
+    
+    Tested with existing telemetrum flight logs and it correctly detects
+    landing in all cases.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 146a0ab223e8d9b376125d1e59f597f6d7851a9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 18:49:55 2011 -0700
+
+    altos: Add ability to read new TELEM files to ao_flight_test
+    
+    Not that telem files are currently very useful as the kalman filter
+    gets completly confused by the variable steps caused by missing data, but...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6795d353be91df96a571cebc237e6a54a065a380
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 16:44:10 2011 -0700
+
+    altosui: Change continutity colors to yellow/magenta
+    
+    Makes them stand apart from the accel/speed lines
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f6cce5749724dbb836aaa27bbeedf977106f6f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 16:43:41 2011 -0700
+
+    Add HARA Bragg Farms site
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6796d9e253a808824ba32cdb008da8bf302780fc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 15:53:13 2011 -0700
+
+    Fix NCR Pawnee location
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ddef3e4ec1b3ff86b164f83807c34c2a78f73eb8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 15:45:07 2011 -0700
+
+    altosui: Mark preload site location with red circles (like launch)
+    
+    Just to show where on the map the official launch location is.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1f3f3d575572eff33a2bc7a53d4691e59a428450
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 15:09:55 2011 -0700
+
+    altosui: Add a bunch more site locations
+    
+    BALLS, Rio Rancho, METRA, QRS Cedar Grove and Hudson Ranch
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fef42e0d9e0a20bdbd32e052749fc63575515e1c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 11:29:13 2011 -0700
+
+    altosui: Add launch-sites.txt
+    
+    Contains a few of our favorites.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 81cac174c80ee42d9e94c6500da7c4c760c3ce67
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 11:25:47 2011 -0700
+
+    altosui: Download list of site locations for map preloading
+    
+    The current URL for this is:
+    http://gag.com/~keithp/launch-sites.txt
+    
+    The format is:
+    <site-name>:<lat>:<lon>
+    
+    lat and lon are both in signed decimal degrees.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f7cd8317bf78ece334e1ceb0263b875ca43bbbd2
+Merge: 51796e2 a482d90
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 08:17:44 2011 -0700
+
+    Merge branch 'preload-maps'
+
+commit 51796e2f1ebce3ee8dc1ac90648381410c1379ee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 23 11:32:29 2011 -0700
+
+    altos, altosui: Add igniter mode (dual, apogee, main)
+    
+    This provides for redundant charges for either apogee or main.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1332ed55192f22525f31574f60fae4c6579e6f7f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jul 17 08:13:15 2011 -0700
+
+    Set version to 0.9.4.4
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a482d904a3f391c3a24df3660acb3f3696aa6766
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Jul 16 23:08:49 2011 -0700
+
+    altosui: Make sure degree and minute values are visible (map preload)
+    
+    Set min size to preferred size so that the value remains visible
+    instead of snapping to 0 pixels wide.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1681c57cbbfc5214dbc2a519e54ce9f29ffe3921
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 22:43:34 2011 -0700
+
+    altosui: Remove a bunch of sitemap debugging printfs
+    
+    Seems to work, let's get less chatty
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0a4d934f6e2914bfe2d965630543f029a1576c11
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 22:34:44 2011 -0700
+
+    altosui: Display full map preload area in view.
+    
+    This involved fixing the map view to support arbitrary sizes, and then
+    exposing a synchronous tile loading API so that the progress bar could
+    be used to show tile loading progress.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit abb8510b97ce9cbbff0275cc31f74780fe1ce138
+Merge: 0929ee3 00e6981
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 21:06:37 2011 -0700
+
+    Merge branch 'scan-telemetry' into preload-maps
+
+commit 00e6981c2e0a668864fcf391932855cd8942140c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 21:05:06 2011 -0700
+
+    altosui: Flush telemetry lines before starting to watch for scan results
+    
+    This prevents pending telemetry lines from being incorrectly
+    attributed to the wrong channel/telemetry.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0929ee32f753255cbe1474988cb41a5a86d29a0e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 17:37:20 2011 -0700
+
+    altosui: Try to avoid resize weirdness with map preloading
+    
+    grid bag + box does some strange stuff, this appears to avoid the
+    worst of the interactions.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 225073fd822f9861a83d65386c29fda9b37bf273
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 16:37:40 2011 -0700
+
+    altosui: Add map preloading GUI
+    
+    Provide a way to manually enter latitude and longitude, preview the
+    map area while downloading a 9x9 grid of map tiles to be used when
+    monitoring flights without network access.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cbd14ba103ee5e3c5eec18e3a4ff13c320b98634
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 20:44:51 2011 -0700
+
+    altosui: Set 'seen' bits in legacy telemetry packet reader
+    
+    Otherwise, the 'scan' code won't show detected flight computers.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e905042879147dd86241bf2dcc7437e5a6eb7578
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 20:43:57 2011 -0700
+
+    altosui: Initialize channel and telemetry before use in ScanUI
+    
+    Otherwise we try to use telemetry format 0, which means 'no telemetry'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 941b90a4905e34936d24a25ca90ac04eb6f5a792
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 17:38:00 2011 -0700
+
+    altosui: Generalize and centralize telemetry constants, parse v0.8 telemetry
+    
+    Move telemetry constants to Altos class, adding functions to compute
+    names and lengths. Generalize users of these values to use all of the
+    known values.
+    
+    Add support for v0.8 TeleMetrum telemetry
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ef786276b5d5c7d17c3fe4f36aa41db61a9742f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 14:23:08 2011 -0700
+
+    altosui: Finish radio scanning UI
+    
+    Scans all channels and telemetry formats, presenting visible devices
+    in a list. Entries from the list may be selected, in which case a
+    monitor window pops up with the appropriate configuration.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ebcba28b3c09925869b617880d2919e5d0e059f0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 14:19:41 2011 -0700
+
+    altosui: Configuration telemetry record includes flight number
+    
+    Mark the reported altos record as including flight information.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d4375bc737655546c2d40f49acdfc2e60ebfea5a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 16 14:19:14 2011 -0700
+
+    altosui: Remove debugging printf from AltosLog
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f32a55ac9a3ebbde2b41782f22491e72258fe05a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 9 19:00:12 2011 -0700
+
+    altosui: Pop up monitor window from scan dialog
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8c20030ea4eb8e068e1ba88e01d07dfbc27bd7db
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 9 18:41:15 2011 -0700
+
+    altosui: Start adding support for scanning radio for available devices
+    
+    This is untested.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7bb11b716ccb6c80701bc3f34ecf9bef97cbbfc9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jul 15 18:53:41 2011 -0700
+
+    Set version to 0.9.4.3 for Bdale 2011-7-16 flights
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0d0cf6f9a1b14a1b66aee3845964cd33d1f035c2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 9 13:09:57 2011 -0700
+
+    altos: new versions of sdcc require __ prefixes for custom keywords
+    
+    Fortunately, 2.9.1 appears to accept either, so we can switch now and
+    prepare for sdcc 3.0.0.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8f80f5705d64469bcfb00ff11aee68364edb271b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 21:38:57 2011 -0700
+
+    altosui: Don't show missing igniter and gps values
+    
+    The new telemetry stuff leaves state.gps always set (but empty), which
+    seems fine, we just need to look at state.gps.connected to see if
+    there's a GPS receiver on board.
+    
+    For TeleNano, we also want to hide the igniter status fields as they
+    won't have any data present.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 80ca066a825646f833ca609190c76c5252118d9a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 21:36:38 2011 -0700
+
+    altosui: Build device constants into .java code
+    
+    This eliminates a depedency on updates to the system helper library,
+    which means we don't have to provide a new library on all platforms
+    just to support a new USB id.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 504ab7ab355652d5d01094c927089029596a0753
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 17:49:01 2011 -0700
+
+    altos: product defines are always in ao_product.h
+    
+    When all products were built in a single directory, each one had a
+    separate version of ao_product.h. Now that they all reside in
+    sub-directories, each directory has its own ao_product.h
+    
+    This change is needed so that other modules in the system can use the
+    product defines; otherwise, ao_product.h was not built at the right time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 81cf2e833bedbc1ace8fd310e9e94bfb7673d428
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 16:43:17 2011 -0700
+
+    altos: Ensure low-rate telem packets interleave with sensor telem packets
+    
+    To avoid over-committing the radio link, we want to send only
+    one low rate packet after each sensor packet. However, the
+    initializations for this were incorrect, causing the configuration and
+    location packets to be sent at the same time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5ca6400fd8a360b64d8f96f50d5595a7fd17762d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 16:18:54 2011 -0700
+
+    altos: Remove ao_telemetry_orig.c and ao_telemetry_tiny.c
+    
+    All products use the common ao_telemetry.c code now
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0154d13756bcb09f009981ee5e4bd27fd04b8788
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 16:19:22 2011 -0700
+
+    altos: Switch Tm and Tn to common telemetry code
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b65140a0139075adeddaccf0f4d5c7a75fac4757
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:52:25 2011 -0700
+
+    altos: Switch flash drivers __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 038d7b25ba833da4be458409670d3f95e8aaf17b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:51:52 2011 -0700
+
+    altos: Switch ao_flight and ao_flight_nano __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3742b36a528f114c3b1873caa4f39581145b76da
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:28:01 2011 -0700
+
+    altos: Switch ao_dbg.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6903b6464db7eb803de8bf9b897c45431f7a1d63
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:27:32 2011 -0700
+
+    altos: Switch ao_config.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6893752900385ee51cc4cf75e1b672202de7578a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:26:09 2011 -0700
+
+    altos: switch ao_cmd __xdata to __pdata
+    
+    Saves code space
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2ec986f08ce8d4635f4435bb0042b405d93edc40
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:45:04 2011 -0700
+
+    altos: Switch const for __code in struct ao_cmds
+    
+    This saves quite a bit of code space when accessing these values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 355db71f4511adff8abcb2caded61c12fe8b7ee9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:41:32 2011 -0700
+
+    altos: Switch ao_usb.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 359baab005c274a0841268c615c23b3ffef813cf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:41:06 2011 -0700
+
+    altos: Switch ao_telemetry.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f2f8ade6994aa3a69fd08f19c4403ceb8cea295d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:39:44 2011 -0700
+
+    altos: Switch ao_stdio.c __data to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9ba0da9247ea424a7a147aa85daae0d5e6316b81
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:38:46 2011 -0700
+
+    altos: Switch ao_serial.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 86b41d4c2b8a9fa4507cdb75302e0cedebb103cb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:38:29 2011 -0700
+
+    altos: Switch ao_sample.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 803bf106caf5d6b5ac12eb00a941647c7325edd1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:37:34 2011 -0700
+
+    altos: Switch ao_rssi.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 002f167fc2709aaf1a4984aaa0a3519a97749d5f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:37:15 2011 -0700
+
+    altos: Switch ao_report.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5203ddaac692bfd82a01368da9fb66c25e9e14c1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:36:45 2011 -0700
+
+    altos: Switch ao_log.c and ao_log_big.c __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 62267144d189967fcd0724b6dfbdbab3cb6fb414
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:33:52 2011 -0700
+
+    altos: Switch ao_ignite and ao_gps_sirf __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 92047ff86c79c2b18ef565a4560b06fe00d6f159
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 15:31:53 2011 -0700
+
+    altos: Switch ao_gps_skytraq and ao_gps_sirf __xdata to __pdata
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 64860be02b3efa6f784a259249cfa6d14545fbd3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 13:49:05 2011 -0700
+
+    altos: Shrink ao_add_task by rolling up a memset loop
+    
+    This has a dramatic effect. By pulling the 'stack' variable into
+    registers it reduces the size of this function from 550 to 231 bytes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 480b48837db31987b947e4d32248965d4a16be03
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 13:21:25 2011 -0700
+
+    altos: Shrink ao_config_callsign_set
+    
+    Replacing a hand-coded memset with a function call, using static space
+    instead of stack space for an array.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 41c230cac359b4459ca93196d08704b7d35447c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 6 12:38:11 2011 -0700
+
+    altos: Shrink ao_cmd_put16, ao_cmd_hex and ao_cmd
+    
+    No functional changes, just reduces code size.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 481577a29380afe6750ef7c4e928daff837cbc49
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 23:38:42 2011 -0700
+
+    altosui: Compress telemetry records marked with the same time
+    
+    Split telemetry transmits multiple packets with the same
+    timestamp. Merge those into a single record when read from a file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7cfd43663cde5ebdf04e4face076d79ff6329ac3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 23:38:28 2011 -0700
+
+    altosui: Remove debug printf.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d0335f83c54df0b23c28d04d34c212a1bdffadd0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 23:37:51 2011 -0700
+
+    altosui: Add main/drogue voltages to default graph
+    
+    Until we get a UI for changing the graph elements, lets add a few more
+    potentially useful values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c1f859170b37864b816eb561318dbfb1cafaeed6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 23:37:00 2011 -0700
+
+    altosui: Elide missing values from graphs
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 72575dcb9cfbb5c1ccdb3510b9962a6f60ca3fa3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 23:35:50 2011 -0700
+
+    altosui: Elide nul bytes at end of telemetry string values
+    
+    All telemetry fields are fixed length, so any embedded strings are
+    padded with nul bytes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6ac34f9c8efd464194137ac4ce8228bf9d7d83be
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 23:35:02 2011 -0700
+
+    doc: Add section about TeleDongle USB line format
+    
+    Describe the format of the TELEM lines sent over USB from TeleDongle
+    to the host.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit edf6252450e06fd42fa6dde3acd127baa8fa6d36
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 21:44:53 2011 -0700
+
+    altos: Adapt to changes in telemetry Configuration packet
+    
+    Apogee delay added. flight_log_max changed to two bytes (in kB now).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 938949e39aac834a1c0912f8f307f74fe41418cc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 21:42:22 2011 -0700
+
+    doc: Chang Config and Location packets
+    
+    Config packets get apogee delay, and have flight_log_max shrunk to two
+    bytes.
+    
+    Location packets get climb_rate added.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4132ac5896114e5f3d8fb3f219422e8933078cf4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 5 21:41:44 2011 -0700
+
+    altosui: Parse remaining standard telemetry packets
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ef3ce687d73c1274ce5368432f4d449b063ce5c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 23:39:21 2011 -0700
+
+    altos: Complete new telemetry switchover
+    
+    This involved rewriting the GPS code to use the telemetry structures
+    directly so that a memcpy could be used to transfer the data to the
+    telemetry packets, saving a bunch of code space, along with fixing up
+    the gps testing programs to deal with the structure changes.
+    
+    In addition, the teledongle code needed to have the monitoring code
+    split into separate radio receiver and USB writer threads as the
+    packets are now back-to-back, and hence come too fast to wait for the
+    USB data to be sent to the host after each one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 359681f23e2f71bc8f4975a4a76ae28c08ecab2e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 18:09:03 2011 -0700
+
+    altos: Add split telemetry code
+    
+    This sends every packet every time, which isn't correct, but should be
+    useful for testing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b51e5466f7a125db873edd1fa9bd3881d7e98aad
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 18:08:19 2011 -0700
+
+    altos: Shrink help text
+    
+    Reduce const space taken by command help text.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9e5e4c1ad82d621ceb7286f72c87eeaf5976f9bf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 18:06:48 2011 -0700
+
+    altos: Add sat info to GPS report command
+    
+    Plan to use this to report current flight computer state in idle mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 06b6f78e22be38a26bfe11ed4d4b659d5b13f00c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 18:04:49 2011 -0700
+
+    altos: Shrink const space in ao_config
+    
+    Eliminate separate 'cmd' and 'help' struct entries.
+    Use \0 trick in cmd strings to eliminate whitespace.
+    Edit help text.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d3c26e534d8df34cfbf29b70cd1b2692493ce150
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 18:03:51 2011 -0700
+
+    altos: teledongle does not need ao_packet_slave.c
+    
+    TeleDongle doesn't provide slave interfaces, so remove ao_packet_slave
+    from the TD build
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c763a3f9cf2bf055e9705ce5ff4bc172f445037d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 18:02:52 2011 -0700
+
+    Version strings must be < 8 bytes long
+    
+    The new telemetry packets include the version string, so make sure its
+    less than 8 characters long
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a08173197d5533ecb395102ed34e751135660d06
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 18:01:59 2011 -0700
+
+    doc: Fix a few minor telemetry doc mistakes
+    
+    Multiple 'accel' entries in the Sensor packet.
+    Swap ground_accel and ground_pres to group accel cal data
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 98df3ba984acf3b47a09949bbea0f3264f711f5b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 4 14:17:55 2011 -0700
+
+    doc: Complete initial telemetry description
+    
+    Finish describing the contents and modulation scheme for telemetry
+    data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 06e82bd2c2a5eea153a053e542df9bc3537e9a01
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 2 01:50:33 2011 -0700
+
+    doc: Add telemetry format description
+    
+    Document the telemetry packet contents.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7fd9b8f720add559b262e81d61ededc9df16ca94
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 28 01:03:00 2011 -0700
+
+    altosui: Support raw telemetry from TeleDongle
+    
+    Use raw telemetry frames when TeleDongle supports them, this involves
+    parsing the hex dump of the packet instead of having teledongle take
+    the packet apart. Only the legacy format is working at this point; the
+    altos bits for the new split telemetry frames is not written yet.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cb239b7161feea8646425b1f5788c3c82ae24321
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 28 01:01:08 2011 -0700
+
+    altos: ao_radio_recv needs byte count *including* rssi and status
+    
+    That's two more than the actual packet length.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 336224a08327cadc95f6e5b564a4ddc64aaad8f8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 24 08:31:51 2011 -0700
+
+    altos: Start adding new telemetry frame definitions
+    
+    These use the initial 24 bytes per frame plan, which will probably get
+    changed to 32 bytes per frame.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5e111fdf1f23203baeeb490ae1b69402ebd513b8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 24 08:31:12 2011 -0700
+
+    altos: Add checksum to TELEM output lines
+    
+    Verify the received telemetry lines to protect against OS data loss
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 489a68ba8e3bc360e2e8fc887e4c4b840b5a0dd3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 22 12:27:34 2011 -0700
+
+    altos: Add arbitrary telemetry packet monitoring
+    
+    This adds the ability to monitor arbitrary telemetry packets (up to
+    128 bytes), moving the telemetry data parsing up to the host.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0e67b6890dd3a06665239f8dfd2e69266d055e46
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 22 12:26:45 2011 -0700
+
+    altos: Rename telemetry to telemetry_orig
+    
+    This makes room to create a new multi-packet telemetry format without
+    changing anything yet.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dc0b49dcbaa2d0a69e002c151337b6e9fd3060d9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 2 23:16:30 2011 -0700
+
+    altosui: Handle old TeleDongle receiving kalman telemetry packets
+    
+    The telemetry packets now send the kalman height/speed/accel values
+    instead of the ad-hoc values. If received by an old TeleDongle box,
+    the speed value will be of the form 0x8000abcd, which will be printed
+    as a 32-bit value by TeleDongle. We only want the abcd part, which is
+    the speed * 16. Detect this automatically and compute the correct
+    values for all three.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4d27e281f81aee88adff3d84085356ec310e4b92
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 25 21:38:02 2011 -0600
+
+    Switch version to 0.9.4
+    
+    For Mayhem, so we can tell what version each board is running
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 479bdffa35d0b8d4e48868c8d20f3cb1549521ab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 23:55:23 2011 -0700
+
+    Revert "altos: Debugging TBT issues -- check pin configuration after boot"
+    
+    This reverts commit 514348055630edec12224c4b0964240b929759a3.
+    
+    Looks like this was never a problem.
+
+commit 66bdf0e066bc0bb7a326a6c2a9c88b69e5c1be66
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 23:12:47 2011 -0700
+
+    altos: clear CPU port 1 interrupt flag when handled
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8be559baa979c15e78f8dba7879b383dbe3936d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 22:59:15 2011 -0700
+
+    altos: Hook up the P1 ISR for TeleBT v0.1 bt_link line
+    
+    Otherwise, we're heading off into the weeds...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 23f441b95e55fbee709382b05d325bc021285766
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 22:49:54 2011 -0700
+
+    altos: Initialize beeper for telebt
+    
+    Needed to get sounds to come out
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 514348055630edec12224c4b0964240b929759a3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 22:42:58 2011 -0700
+
+    altos: Debugging TBT issues -- check pin configuration after boot
+    
+    Make sure the serial pins are configured as peripherals
+    Make sure the ser_reset and bt_link pins are going the right direction.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 22e3ac0eb014b8255029763ae8180ad3527ba306
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 22:42:32 2011 -0700
+
+    altos: Add beeper to TBT v0.1
+    
+    It's available, let's use it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6d858b64ee0e8c227c149d2af6d2d634536964f4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 22:12:31 2011 -0700
+
+    altos: pull TBT v0.1 ser_reset line low
+    
+    This line resets the BT module if held low for three seconds.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3336d0f726afd1d43cf62280940e5fb91dab2e91
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 21:13:19 2011 -0700
+
+    altos: Fix BT link status pin for real TBT hardware
+    
+    The prototype used P2_1, while the real hardware uses P1_7. Lots of
+    defines to make this work...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 19bfa3882a2d95fcade256c2d63ad24f794281e5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 20:33:58 2011 -0700
+
+    altos: Use USART configuration 1 with flow control for TBT
+    
+    It's just wired that way.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d9cc27641fe1778c098b065bf110be7823e6c9f4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 16:57:38 2011 -0700
+
+    altos: Add telebt-v0.1 to Makefile
+    
+    Doesn't get built without it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c360e5c04e30421e9ea2f7006b7d35aef35f1edd
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri May 6 17:57:17 2011 -0600
+
+    first cut at a telebt turn on script
+
+commit c1760cebd47d0f03808f3204c0fcb1183f754e50
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 6 15:44:56 2011 -0700
+
+    altos: Add preliminary telebt v0.1 defines
+    
+    This should make the telebt v0.1 binary that works on the real hardware.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 72a03baa73698fc1213a74320e6253c2380dd8fa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 25 23:00:42 2011 -0700
+
+    altosui: Fix BT manage dialog so that the device lists resize
+    
+    This makes the device scrolling lists fill any extra space when the
+    window is resized.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e3bf13a38d24e95b16df1e2f01952d10f24cda10
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 25 22:55:08 2011 -0700
+
+    altosui: Move AltosIgniteUI device open out of Swing thread
+    
+    Eliminate more blocking code from the Swing thread.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 109344d54d3fa4f79342fd1ea2a3f4085475e30c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 25 22:28:40 2011 -0700
+
+    altosui: Display reader name (usually the device) when an I/O error occurs
+    
+    Access the reader name directly from the reader object instead of a
+    local variable (which wasn't getting set anyways).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aa5caf6310f074109472e6f55d8bd9751fb75c4c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 25 21:26:21 2011 -0700
+
+    altosui: Fix TeleBT name in flight monitor title
+    
+    Was getting the product number, not the product name.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2e7b7b80432bb251ac39efa1fa05d32b5f250e14
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 25 21:17:07 2011 -0700
+
+    altosui: Separate out flash debug code to separate thread
+    
+    This avoids blocking the Swing thread while waiting for the serial
+    device.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 214cd69c0e4a1617ed5cde8fc2f46a4cee6ecced
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 23 22:50:58 2011 -0700
+
+    altos: add telebt-v0.0 Makefile
+    
+    Git doesn't like to add these.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d41edb3384b6336f3482e61b0c9f9400a8b4f519
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 15:29:39 2011 -0700
+
+    altosui: Make flight data download work through TeleBT
+    
+    This required flushing input before reading data blocks and adjusting
+    some delays.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 44fb71ca3e5bccd5f601fc5a2d5da7292050b1d6
+Merge: 2ebdb88 c269e26
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 14:06:39 2011 -0700
+
+    Merge branch 'telemini' into telebt
+
+commit 2ebdb888f6792de70b3132950a988d49752d264e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 14:01:19 2011 -0700
+
+    altosui: Eliminate ao_cmd_filter hook
+    
+    Disabling status messages means we don't need to filter them out of
+    the input stream.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f5c9986dfa2d130b6c8c14308638cce49391a6e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 13:42:30 2011 -0700
+
+    altos: remove BT logging code
+    
+    This was used to debug the serial line startup.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0195ff442c1da5d363dfda3f88f41865d0c6b469
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 13:31:10 2011 -0700
+
+    altos: Remove bt debug command
+    
+    This will permit some additional cleanups.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce7cf0c5ddc5405f6f474f4e20752fc0b02f3ecb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 13:27:40 2011 -0700
+
+    altos: Simplify BT communications
+    
+    Eliminate stdio I/O wrappers
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e3d501940718428135e04995dff7fef691c08a20
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 13:20:19 2011 -0700
+
+    altos: Solidify BT connections
+    
+    Use delays while sending commands to BT module.
+    Don't use BT for stdio until the module is initialized.
+    Add \r to name setting command
+    Don't require 'connected' signal for command input.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6b5957d5f6f8181da7be98c9bce49a0ec0b4a713
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 10:41:28 2011 -0700
+
+    altosui: Wait two seconds after bluetooth connect XXX
+    
+    "something" isn't quite ready to communicate right after the device is
+    connected, so we stick a delay in. There should be a better fix.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8de9d3cbfcd1db7b554fb761296a8de09aafc8c3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 10:25:47 2011 -0700
+
+    altos: Add delays to bt startup sequence
+    
+    The BT device takes a few seconds after power-up before it is ready to
+    receive commands.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 17f38e045fcd8ca0224095c0b2b7b098df77a8d8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 19 08:43:40 2011 -0700
+
+    altosui: Use persistent list of bluetooth devices for device dialogs
+    
+    Store a list of known bluetooth devices as preferences. Always include
+    those in device dialogs with an option to go browse for more devices
+    in both the device dialog and the Configure AltosUI dialog.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f249e5926f5fd9f86c41e7f0a414193533d4d8b0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 18 18:16:38 2011 -0500
+
+    altosui: Make bluetooth dialog modal
+    
+    This allows it to be displayed correctly while the device dialog box
+    (also modal) is up.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 84163eee7847a09fe78f8762b28f857d76bf5755
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 14 10:22:30 2011 -0700
+
+    altosui: Make AltosBTDevice implement AltosDevice interface
+    
+    This will allow the use of either USB or BT devices through the
+    AltosDevice interface.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9cdef76c1275b343099d0d01af82d7eadd36a410
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 14 10:12:29 2011 -0700
+
+    altosui: Create abstract AltosDevice class
+    
+    This will wrap either USB or BT devices. The USB device constants have
+    been moved to Altos.java
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5b3f18b38d80aa041b971204bf7a94278bd9584a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 8 19:46:15 2011 -0700
+
+    altosui: Add primitive bluetooth device manager UI.
+    
+    This isn't useful, but does inquire for available bluetooth devices
+    and show them in a list.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c269e263a6accd815ed5d08c0f5a6c3d5b9d3853
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 14 09:38:48 2011 -0700
+
+    altos: Write a few pre-launch samples for Tm/Tn devices
+    
+    Record pre-launch samples in a small ring and flush that to flash when
+    launch is detected. This provides a complete record of the flight,
+    rather than simply starting after launch detect.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a0fb471ce10642fc4a4bd40e4a81f8d6fe7a7c21
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Apr 13 20:27:38 2011 -0700
+
+    altosui: oops - lost state changes when downloading eeprom data.
+    
+    This would cause the reader to just keep reading past the end of the
+    flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f49d694e776819e03b2c708e1c4ee23ba311430
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 9 22:53:12 2011 -0700
+
+    altos/altosui: Log averaged baro sensor data in Tm/Tn
+    
+    Instead of logging the best height guess from the kalman filter, log
+    barometer data. The logged data consists of the average value betwen
+    log points to reduce noise.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8dd455204cf8712fa8c142b0c0517cec1bf5fd0f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 8 10:13:55 2011 -0700
+
+    altosui: Add low-level Bluetooth APIs
+    
+    Adds the JNI functions to query and connect to arbitrary
+    bluetooth devices.
+    
+    Adds Java wrappers to construct a list of proximate bluetooth devices.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf1c7df5301a1727e871a8447f835fe75bdce3fc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 8 10:12:50 2011 -0700
+
+    altosui: Add TeleBT USB device support
+    
+    TeleBT can work just like a TeleDongle over USB.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a54a58d72147888f783a3caf364479efff4ed9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 7 22:00:38 2011 -0700
+
+    altos: Use PIO(6) on BTM to monitor BT connection. Fix BTM init.
+    
+    PIo(6) appears to be an active-low indication of the Bluetooth
+    connection status. Hook this up using an interrupt to track the
+    link state instead of using in-band status messages.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f28efe271f9670473249574f6bcf6e160fe58c7b
+Merge: 8db5c52 835ab3a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 19:35:22 2011 -0700
+
+    Merge branch 'telemini' into telebt
+
+commit 835ab3a8c2741a09b27de58c37439a193c9919ce
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 19:35:00 2011 -0700
+
+    altosui: Add missing AltosTelemetryMap.java file
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8db5c52f1c76a05020e4e0afbe4ea27485ad9f82
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 17:26:08 2011 -0700
+
+    altos: Clean up BT serial communcations
+    
+    Disable echo on both ends in command mode to eliminate looping error
+    values.
+    
+    Switch to 57600 baud to improve performance.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a5d60fdb9c969c1516feb76a16001c9688112c4c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 17:25:07 2011 -0700
+
+    altos: Make cmd echo per-connection instead of global
+    
+    Allow different connections to use different echo values, permitting
+    the packet link to turn off echo while the USB link still has it on.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4e2c18249e16c98cf5f7dccdf8d3b84bc473863a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 16:48:12 2011 -0700
+
+    altos: Clean up serial initialization
+    
+    Flush serial input buffers when switching speeds.
+    Ensure pin configuration is correct.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 92386f2e8419c4df125692cc998eb72ec49bf991
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 14:12:56 2011 -0700
+
+    altos: Clean up usage of serial port for stdio
+    
+    Code wanting to use this must invoke ao_add_stdio; that way
+    the link can be configured before command processing starts.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9f8a96a8516e13878b329dbf1da855ed9a3219c4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 14:11:44 2011 -0700
+
+    altos: Make ao_serial_drain public
+    
+    Allow external code to discard serial input
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4f243a282f9aeb7433ccb2942850d380a091e603
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 14:10:37 2011 -0700
+
+    altos: Remove serial monitor command
+    
+    This takes up space and isn't that useful these days
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 39bde78edc863d9d2ef50a59b8f28ab6274892b4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 14:08:37 2011 -0700
+
+    altos: Allow any stdio to be used with packet forwarding
+    
+    There's no reason to restrict packet forwarding to work only from
+    USB.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 01952da35a57ae4da062facb26b3c6d7de29190f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 14:07:23 2011 -0700
+
+    altos: Provide for a pre-filter on commands
+    
+    This allows for external code to see each command line before it is
+    processed and potentially skip it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 359ba0d9fc2c5947e6adc98bebcd061069c61e79
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 14:04:58 2011 -0700
+
+    altos: expose set of available stdio values
+    
+    This lets external code manipulate which connection to communicate
+    over.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce18eaa28b1385c962c09459cbc5f20e234d9ad5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 1 14:03:25 2011 -0700
+
+    altos: Add P2SEL_*_MASK defines to cc1111.h
+    
+    These are used to avoid having the code "know" which selections are 1
+    and which are 0 bits.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8e74cf6d1c70a7a17d01c20f6831571245392498
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 31 20:58:14 2011 -0700
+
+    altos: Add initial TeleBT code
+    
+    Prototyping with a TeleMetrum v0.1 board and a serial link to a
+    bluetooth module.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c0971abc02b05d136aea257f3f40ba3b22b1d441
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 31 20:55:18 2011 -0700
+
+    altos: Make ao_flight_test show true height but report saturated height
+    
+    To simulate a saturated baro sensor, clip baro data at a specified
+    altitude. Continue to report the 'true' altitude in the output so that
+    the resulting graphs are useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8a14142e7b37031a51409f121b913fe793bf3603
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 31 20:53:12 2011 -0700
+
+    altos: Baro useful ceiling is MSL, not AGL
+    
+    Use MSL instead of AGL for detecting over-range baro sensor values.
+    Always trust baro sensor during descent; it'll get there eventually.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9dae18a664f70b668159487015e61be7f776926
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 31 20:51:15 2011 -0700
+
+    altos: Reflect ao_flight split in ao_flight_test dependencies
+    
+    Need to rebuild ao_flight_test when any of the flight sources change
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f558cfa1df77c36a459168c1953d0945ee5a7f9f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 30 11:48:03 2011 -0700
+
+    altosui: Only plot acceleration when present in data file
+    
+    Eliminates a bogus axis and data line for devices which do not have an
+    accelerometer.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9df9fc257eb2d7038d66ac7c2539aae4474bf12
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 30 11:47:07 2011 -0700
+
+    altosui: Parse and export Max flight log value
+    
+    New configuration field might as well get dumped to the .csv files.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 87bff181a95f6bf92c2cec350d331ba6af779e80
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 29 18:11:47 2011 -0700
+
+    altos: Enable logging during nano flights
+    
+    Not having logging wasn't very useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 011e37f27b3926a42c8c1a74e0f179bb48829ec7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 29 18:10:46 2011 -0700
+
+    altos: Run RDF beacon after apogee instead of waiting for landing
+    
+    This provides tracking when GPS fails, or on TeleMini.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 08e6bbef2c3529dfd468ef221c526fc9f3ed5b81
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 29 10:08:46 2011 -0700
+
+    altos: ao_sample_preflight was exiting preflight mode immediately
+    
+    Need to stay in pre-flight mode until we've gotten enough sensor data
+    to calibrate things appropriately. The conversion from a unified
+    ao_flight.c file was just broken here.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 56d045040c49728a854741e99545766f3723da5e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 29 09:39:27 2011 -0700
+
+    altosui: Don't display 0000-00-00 for missing flight log dates
+    
+    With Tm/Tn not having GPS to get the current date, it's no longer
+    unusual to have no date for a flight log, so don't show the 0000-00-00
+    piece in that case.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8ade7d99f02df825e70d0a964b4648156101ef78
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 29 09:38:23 2011 -0700
+
+    altosui: Display exception messages from swing thread
+    
+    Flight log management exceptions were getting displayed from the log
+    serial I/O thread instead of the swing thread. That's a bad plan.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2f9be009ef26e3d7539f5932d267d7a8a7bcb7eb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 29 09:37:11 2011 -0700
+
+    altosui: Make deployment testing handle Connecting... dialog
+    
+    Supporting the Connecting... dialog requires moving all serial
+    communication to a separate thread. This was done by creating a worker
+    thread and command queue to communicate between the UI and the serial line.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 573edcd7dfe10ac3251396eae88eece55d82bcb6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 28 23:38:02 2011 -0700
+
+    altosui: Make flight log downloading handle 'Connecting...' dialog
+    
+    This required moving all of the serial communication to a separate
+    thread and making the bulk of the download operation run after that
+    has finished.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c71a145daefb86d2c1297abec68e54bd951e3adf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 28 23:35:05 2011 -0700
+
+    altosui: Clean up packet link connecting dialog
+    
+    Make sure the dialog is destroyed after use (otherwise, it hangs
+    around on the screen sometimes).
+    
+    Switch timeout before showing dialog to 500ms -- that brings the
+    dialog up less often when unnecessary.
+    
+    Use 'timeout_started' boolean to indicate whether the I/O thread has
+    queued the dialog for display and whether it needs to queue a call to
+    close it down.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c6e7e812d67f91c63ba4982f7a899a72584027de
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 28 18:18:50 2011 -0700
+
+    altos: Create custom nano flight code
+    
+    No igniters, just 'pad/drogue/landed' modes (where 'drogue' ==
+    'flying'). A constant 1Hz telemetry and RDF rate.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c754759a2d503633d527da4ebb20eb859cd506fd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 28 17:54:44 2011 -0700
+
+    altos: Split up flight code into separate flight/sample/kalman bits
+    
+    The flight code mashed together data processing, filtering and actual
+    flight managament into one giant pile. Split things up so that we
+    have:
+    
+     ao_sample.c: Sensor data processing. Reads the ring, handles calibration
+     ao_kalman.c: Filter the data to track the accel/speed/height values
+     ao_flight.c: Flight state management, specific to rocketry.
+    
+    The plan is to re-use ao_sample.c and ao_kalman.c for hardware not
+    specifically designed for rocketry, like TeleNano.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 006de838bbb096b9443863a46b8a125b1e6b5600
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 27 00:48:07 2011 -0700
+
+    altosui: Handle serial calls from swing thread
+    
+    Calls from the swing thread cannot be canceled as there's no way to
+    put up the cancel dialog. In this case, simply use the 5 second
+    timeout and fail if no communication occurs within that amount of time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f23d0f3cbf1fb0c8eab497e266625f6410b69ba3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 27 00:46:19 2011 -0700
+
+    altosui: Tell serial device which frame to use for timeout dialogs
+    
+    For the timeout dialog to appear, a frame must be configured for it to
+    appear near. This patch sends the frame from the eeprom download
+    functions to the serial code. That path doesn't yet work as the eeprom
+    download is still trying to talk to the serial device from the swing
+    event thread, which prevents the cancel dialog from working.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 067b21993e9a97fceadb355e571e5610535336a8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 27 00:13:38 2011 -0700
+
+    altosui: Allow radio channel to be configured over the radio link
+    
+    TeleMini/TeleNano can't be configured via USB, so we need to allow
+    the radio channel to be set over the radio link.
+    
+    This change carefully sets the new radio channel, disables the remote
+    link and then sets the teledongle channel to the new value and brings
+    the link back up.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 91a75279b6d306ba9d068a28c64917d5312122e8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 27 00:12:01 2011 -0700
+
+    altosui: Off-by-one error in telemetry format configuration UI
+    
+    The telemetry format menu uses 0 for full and 1 for tiny, but the
+    telemetry configuration uses 1 for full and 2 for tiny. One direction
+    (config to UI) was right, the other (UI to config) was wrong.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ce8c9081e703d1405c2595ab9bda0cfa218c6c4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 26 23:38:54 2011 -0700
+
+    altos: full logging must flush pending data before checking state
+    
+    Flight state must be checked only after any pending data have been
+    written to the log as the 'current' flight state is only valid when
+    the pending data values have been processed. This ensures that the
+    'boost' state is not marked until the full ring of data is
+    written. This ensures that the data processing code can find the
+    barometer values from before boost to get an idea of the ground
+    pressure value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3945d8f986d8f4bd3186a2cbaed5186e49d59839
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 26 23:15:36 2011 -0700
+
+    altos: Variable log rate in full logging code too
+    
+    With the fixed ADC rate used to get better data during flight, the
+    logging code now needs to vary the data storage rate so that descent
+    data is recorded at 10 samples/second while ascent data is recorded at
+    1 sample per second. Having the logging code do this itself eliminates
+    any interaction with the flight code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97517ee585462c2d355f23f999fb8d9ebd908ec1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 26 00:01:22 2011 -0700
+
+    altosui: Allow TM config connection to be canceled.
+    
+    This leaves the config UI connection attempt running and pops up a
+    dialog box when it takes 'too long' in the remote case so that users
+    with Tm or Tn devices can bring up the UI, and then boot the Tm/Tn
+    without needing to time things carefully.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2c121f1ef495e8af3eb39210baa40e212b691894
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 25 22:04:09 2011 -0700
+
+    altosui: swing hide/show methods are deprecated
+    
+    I don't know why, but they are, so just replace them with
+    setVisible calls.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b155647472ddfacb07c5ffa832e4d1f4a13ad342
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 25 22:01:18 2011 -0700
+
+    altosui: Remove extra AltosEepromBlock layer
+    
+    This was interposed between the download layer and the eeprom layer to
+    hold a eeprom block full of flight log records. The addition of the tiny
+    log format required reworking the code to hold chunks full of eeprom
+    data without regard to their content, so this content-specific layer
+    didn't seem useful anymore.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 011615d40b3cb1d1c0ab9fa41e139e263a6a51e7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 25 21:34:31 2011 -0700
+
+    altosui: Add support for downloading TeleMini/TeleNano flight logs
+    
+    Splits the eeprom downloading code into eeprom block downloading and
+    separate eeprom data parsing so that the new data logging format can
+    share the data downloading code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dea80af81b388cc3d7073444919f4e98b12fa730
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 25 21:29:50 2011 -0700
+
+    altosui: Remove a bunch of debug printfs from the eeprom manager code
+    
+    Just noise on stdout.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f5b5848ad6ef5c808638a29c3dc0101b56ed11e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 24 08:08:43 2011 +0900
+
+    altosui: Add telemetry format menu and preferences
+    
+    Switches the TeleDongle between full and tiny telemetry packet
+    formats, saving the last used format for each teledongle in the
+    application preferences.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3e68341f6f5daaf26dd162e4f9a06c29988986a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 24 05:27:57 2011 +0900
+
+    altosui: Add support for telemetry version 4
+    
+    New telemetry format needed to support TeleNano and TeleMini
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1e976a105423f2da1842f70da531c9051ba88a7f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 23 10:42:31 2011 +0900
+
+    Add description to test flights
+
+commit 93040ef4d0bd90ec5ae052f22243cd56adfb300a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 23 10:40:05 2011 +0900
+
+    Add ao_kalman.h to .gitignore
+
+commit 43a94380032300a2e33e1faa1efe93e858e0a2cf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 23 10:37:39 2011 +0900
+
+    altos: Exit flight test at landing. Allow description in test flight list
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 32364c9e0d346e0e5d517e18d4e90b8ff2fa944f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 23 10:33:38 2011 +0900
+
+    altos: Ignore alt error for fast->coast. Allow larger error for baro apogee.
+    
+    With the fixed kalman filter, transitions across mach don't cause
+    bumps in the merged filter.
+    
+    And, with working kalman bits, the signal for broken baro detection is
+    stronger and so we can allow for baro apogee detection in cases where
+    noise occurs close to apogee.
+    
+    Bump the kalman filter to trust the baro less so that the model tracks
+    across mach.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3d2042ccc2d29e4cb8ea39c5c69d07cb7e3daeea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 16:55:08 2011 +0900
+
+    altos: Restore sensible kalman values
+    
+    Now that the kalman code seems to work correctly, restore the sensor
+    errors and model errors to match reality
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7d7b476564a16eda81ab3406f70a21995e1b464e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 21:51:52 2011 +0900
+
+    altos: Fix up flight code testing
+    
+    This automates flight code testing by reporting mis-detected apogee or
+    main events.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a80d3836cfce3d4cfa7a71068539415c2dc421cd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 21:50:29 2011 +0900
+
+    altos: Missing parens and some bad arithmetic in the kalman code
+    
+    Fixed point computations are a pain.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f30de5766c1eefb18c7d024a2cf10ce02de41071
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 21:29:05 2011 +0900
+
+    altos: Add ao_flight_debug code
+    
+    Trace the kalman filter to make sure it's working.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3053b1f3c85d4fd84b3c6cc87858f433166df34
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 17:04:07 2011 +0900
+
+    altos: Clean up some debug stuff in ao_flight.c
+    
+    Remove some spurious printf debugging.
+    Remove an attempt at discovering broken accelerometer code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6864e06d88a5b908cffa7c4cd2be8969ff46ce4d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 16:51:04 2011 +0900
+
+    altos/kalman: Kalman terms can be > 1, use 32-bit fixed point
+    
+    Because speed and acceleration are scaled by 16, it's fairly common
+    for the kalman terms to end up larger than 1. Instead of trying to
+    fuss with 16-bit values and shifts, just use 32-bit values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c14d6c5ace1d67bd948273ceb7eb6807b29c3806
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 08:51:23 2011 +0900
+
+    altos: Compute a 'trust' value for the barometer
+    
+    Instead of making the baro use/don't-use decision binary, use a 'trust
+    value' which slowly migrates from baro+accel to accel-only mode. This
+    eliminates bumps in the data from a rapid shift.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7b009b2efe3af8722c358c304c2243652594e0d5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 22 05:42:51 2011 +0900
+
+    altos: Switch telemetrum over to kalman filter
+    
+    This changes the full telemetry stream to include kalman data instead
+    of the old ad-hoc flight data. It's compatible in that the packet
+    sizes are the same so teledongle can receive either and figure out
+    which it has received.
+    
+    A few plotting and testing tools are added to make validating the new
+    code easier.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 20427ae4965f756aac0cedc5179a1c45b9a781f2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 21 19:59:27 2011 +0900
+
+    altos: Add nickle kalman implementation.
+    
+    This generates the constants needed to implement Kalman filtering in
+    the flight firmware.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca3f03ef5c09446bebf0f5734f36a0248c457b1d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:55:39 2011 -0700
+
+    altos: Add .sdcdbrc file for teledongle
+    
+    My sdcdb script uses this to set command line options automatically
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit be838db49d999426a9dd02c0166fe161722f1e61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:53:08 2011 -0700
+
+    altos: New telemetry report format (version 4). Supports tiny telemetry.
+    
+    This completely replaces the version 3 format with a much simpler and
+    easier to parse scheme. It's described in detail in ao_telem.h, but
+    the basic idea is that the whole line is split into name/value pairs,
+    separated by whitespace. Every name is unique, and the values are
+    either strings or integers. No extraneous formatting or units are
+    provided.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ad6bb342d237988404fa32540b38c61d6ddc1f0d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:51:02 2011 -0700
+
+    altos: The kalman code requires a constant sample rate
+    
+    The kalman function can't handle a variable sample rate, so keep the
+    ADC running at full speed for the whole flight instead of slowing it
+    down after apogee.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 31feb7777f73fed61193d3404f457ea1a081fe9c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:49:41 2011 -0700
+
+    altos: Split telenano main from telemini
+    
+    Eventually, telenano will run different code; prepare for this by
+    creating a telenano-specific main routine.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8950df02382f5f0aea5bac078fdf7134b98c43ed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:46:18 2011 -0700
+
+    altos: Split out tiny telemetry from full telemetry
+    
+    The TeleMini and TeleNano boards do not have either GPS or
+    accelermeters, and they also run the kalman filter which produces
+    standard unit measurements for the flight height/speed/accel
+    values. This makes the telemetry significantly
+    different. ao_telemetry_tiny.c sends the required data.
+    
+    Note that TeleNano sends the same telemetry as telemini at this point;
+    there are a couple of values which are not useful, but the overhead of
+    sending them is small enough that the hassle of having three telemetry
+    formats seemed excessive.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5ba75e95c98d3e441a58d6f75d328d579e1997fe
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:41:44 2011 -0700
+
+    altos: Make telemetry interval more consistent
+    
+    Instead of using a delay between telemetry packets, use a telemetry
+    period and compute an appropriate delay each time. This requires
+    changing the ascent telemetry from a 50ms delay to a 100ms interval,
+    to provide a regular 10 packets-per-second rate. Before, we counted on
+    the telemetry packet taking about 50ms to send so that we would
+    receive about 10 per second.
+    
+    This also eliminates delays during descent for RDF tones -- those will
+    get transmitted in the interval between telemetry packets without
+    interrupting the spacing of those packets.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3f0bc801fd08a613c681504f0d1f9374486a2487
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:31:20 2011 -0700
+
+    altos: Configure packet size from send/recv parameters.
+    
+    Instead of setting the packet size at configuration time, use the
+    provided packet size to the send/recv functions to configure the
+    radio. This eliminates many configuration calls, leaving us with 'RDF'
+    mode and 'packet' mode, the latter working for telemetry and the
+    bi-directional link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5c28b9312d90a3a66016abc641c20bcd852d69f8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 23:27:15 2011 -0700
+
+    altos: Don't init packet slave on TD. Make slave start optional
+    
+    Oops. TeleDongle was starting the packet slave code, which kinda
+    wrecked its ability to receive telemetry packets. This patch simply
+    removes the packet slave code from teledongle as it cannot be used
+    (yet), it also makes the packet slave code initialization take a
+    parameter which controls whether to start that by default; in the
+    future, perhaps TeleDongle will gain a command to start packet slave mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e980b251e5a4d25410710a9aa89ef940e06b0d93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 18:43:52 2011 -0700
+
+    altosui: Add software version to Configure AltosUI dialog
+    
+    Show this somewhere so we can figure out what is installed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1aeb759c48f475ffaaae787515e080440c8386c3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 19 12:28:08 2011 -0700
+
+    altos: Baro-only boards must not detect launch on accel or speed data
+    
+    The baro sensor generates too much noise to use small changes in
+    computed speed or acceleration to cause a false launch detect.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7a4f6d5ad55637cde97a1e2f247f92df59bc2e14
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 18 21:01:15 2011 -0700
+
+    altos: Write height values to log for nano/mini
+    
+    This is a lot more useful than the old filtered pressure data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c985bb6a19c710409629f3c095332ba7afcf5248
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 18 20:36:59 2011 -0700
+
+    altos/test: Add scripts to run lots of flights through the code
+    
+    This runs a long list of flights (there's a user-specific path
+    pointing at the flights) and squawks if the baro and dual flight
+    computers don't match.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dbe915795c66995805b5f37e6eb698cf2c143e61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 18 20:26:12 2011 -0700
+
+    altos: Fix mini/nano default log size to available flash space
+    
+    Also, remove accel cal code from boards without accel
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5db94e1e230bade966a997aa83165405a9ec9d83
+Merge: 1a8f45e cbb968f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Mar 18 21:12:39 2011 -0600
+
+    Merge branch 'telemini' of ssh://git.gag.com/scm/git/fw/altos into telemini
+
+commit 1a8f45e7b720d01d3ff0c35ed1caaf8cbe0c3119
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Mar 18 21:12:12 2011 -0600
+
+    fix up script to work and have reasonable texts
+
+commit cbb968f5cf03625d453d84dc535758072a2c04c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 18 20:07:25 2011 -0700
+
+    altos: Add TeleNano support
+    
+    This just uses the TeleMini bits, which should work fine for now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 32c51840c792a737019fbc9fe42f2ca073b71827
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 18 19:49:46 2011 -0700
+
+    altos: Tiny logging fixes. Scan at start, stop when land or full.
+    
+    Initialize the flight log for tiny systems by scanning the log area to
+    find the current flight number and log area bounds.
+    
+    Stop logging data when the flight is over, or when the log area is
+    full.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 62eae8a17d870e8ac6937ba23da01a5fbc652c6c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 18 16:53:11 2011 -0700
+
+    altos: Add kalman filters for baro-only boards
+    
+    This adds a baro-only kalman filter to track the state of the rocket,
+    and then uses it to control flight events instead of the existing
+    ad-hoc mechanisms.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c826fab31f8aea25a942b6bb8435d4b04c1bef10
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 17 16:00:10 2011 -0700
+
+    altos: Add tiny logging for TeleMini/TeleNano
+    
+    This splits the logging code into management of the log space within
+    storage and separate code to actually write suitable log entries.  A
+    new log writing module, ao_log_tiny, is added which writes only
+    altimeter data at a fairly low data rate for devices using on-chip
+    storage.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8b546b474b7b6c5b4169b4c1ca09c6f17ebb3ae5
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Mar 11 21:41:01 2011 +1000
+
+    ignore new flight test file
+
+commit 82707a05af0eb2d54f46b58805c95cdf4e5a3703
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 16 20:36:50 2011 -0700
+
+    altos: Internal flash ops block when running from flash
+    
+    The docs say that if you are executing from flash, then the CPU will
+    stall after a flash write or erase command is started until the
+    operation is complete. Take advantage of that to simplify the flash
+    code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1d8579f973bfe1047ee91f03555e74abdc483e69
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 7 16:31:43 2011 -0800
+
+    altos: oops -- altitude reporting wasn't pausing between signals
+    
+    need to actually alternate the LED/tone with some space so you can
+    count.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d007bccf6cb36d24a9c7c48de7d80759ac6f2e37
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 7 15:57:58 2011 -0800
+
+    ao-load: Make usb descriptor rewriting optional
+    
+    TeleMini and TeleNano don't have USB descriptors to rewrite when
+    loading firmware, so allow them to be missing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 57d83f51377fb58018f422e42d74f29b86a821d2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 7 15:26:41 2011 -0800
+
+    bringup: Add script for telemini
+    
+    Copied from teledongle with a few obvious changes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ddd7485f05d0cad8f5b3e1ee9b9a4d2812ea1837
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 7 08:03:11 2011 -0800
+
+    altos: Switch pins around for TeleMini
+    
+    TeleMini has fewer sensors and uses P0 for igniters instead of P2.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f8afc2641c779fc312a42a6358187d8716ebe61a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 6 23:52:11 2011 -0800
+
+    altos: Switch LED usage for TeleMini around
+    
+    We're using the LEDs instead of tones, so make red mean 'low tone',
+    green mean 'middle tone' and both mean 'high tone'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fc5d014721a7e5a7b22f07eb4ab0bb3c764473fe
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Feb 26 16:06:48 2011 +1000
+
+    ao_intflash: Avoid overwriting code
+    
+    Require firmware to specify the end of its codespace in its Makefile,
+    and use this to determine where the start of available flash is. Should
+    give compile time errors if either there's no room left for storage, or
+    if there's not enough room for code.
+
+commit 0e4c55d78852415e79f7318471f4d00c89703b78
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 6 21:03:57 2011 -0800
+
+    altos: Add TeleMini v1.0
+    
+    This adds initial code for the telemini board, a two channel
+    flight computer with digital telemetry and a barometric sensor.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2d41358c80f2eb8b6e98d699149bb941a6671475
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 6 21:00:52 2011 -0800
+
+    altos: Start with packet slave running. Turn off in pad mode.
+    
+    Instead of turning slave mode on in idle mode, start with it running
+    and disable it in pad mode instead. This means packet mode is
+    available in startup mode too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1e56ed44e562f808addfd76bfb352f981db94094
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 6 21:59:08 2011 -0800
+
+    altos/test: Add baro-only flight test program
+    
+    This builds the flight code in baro-only mode for testing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e339ffd8bd8b9e3f4758017ba355028000cb612e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 6 21:57:52 2011 -0800
+
+    altos/test: Use ao_convert.c instead of hand-coded pres → alt func
+    
+    Fix up ao_convert.c so that it can be used within the flight test code
+    instead of having a (broken) copy of the code there.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 02611efea0c485d78fad08c696c1f56e868d36b8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 6 20:56:25 2011 -0800
+
+    altos: Make serial, usb, beeper and accelerometer optional components
+    
+    Not all boards will have these, so fix places that use them to deal
+    with that.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fdd15a254c6fab5ba2d02320ba0ceb3e6a56354c
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Feb 26 11:48:30 2011 +1000
+
+    ao_intflash: Use internal flash for storage
+    
+    Makes any free pages at end of CC1111's internal flash available via
+    the ao_storage API.
+
+commit 8f1bd11b61d9423c62162f7bbe573fc69fd75269
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 16 14:10:06 2011 -0700
+
+    aoview: remove -s option.
+
+commit 45395c7825184efb835d5b165fa132be20c7f6fe
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 16 14:08:42 2011 -0700
+
+    Bump published version number to 0.9.1
+
+commit 9f3d26cadf37880d2c9223f59271d295b11c4c2a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 16 14:05:13 2011 -0700
+
+    altosui: Missed jcommon.jar in the Mac OS install image
+    
+    This caused graphing to fail on Mac OS X
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6e340c87d3198647cf075ed520a82703b0d59beb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 7 00:26:17 2011 -0800
+
+    altos: Oops. Lost a couple of commands when merging the doc patch
+    
+    I didn't merge this carefully enough and managed to lose the 'f'
+    and 'e' commands, which are kinda useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8cdf4fb051c22b35c251d90bc288551f7c2898bf
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Feb 27 11:11:12 2011 +1000
+
+    src/ao_cmd: Shave off bytes from doc strings
+    
+    Switch to using { func, "X args\0Desc" } to specify command, saving
+    a char field by looking at help[0] instead, and reduce help length by
+    doing alignment with printf instead of hardcoded spaces.
+
+commit 2cfe205de4242398e69c9e7c613af0d2a7094686
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 7 00:01:01 2011 -0800
+
+    Revert "src/ao_gps_skytraq.c: Update logging rate to 10Hz"
+    
+    This reverts commit b080e933a65d268aaaec8cfd5f617a13d5babc43.
+    
+    10Hz data isn't any better than 1Hz data; it still doesn't like going
+    upwards rapidly.
+
+commit 249cd3b63d97581b068fff988e0cd7fcd5bf493e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 19 01:06:01 2011 -0800
+
+    altosui: Display eeprom parsing errors to user
+    
+    When reading the eeprom, any parsing errors (most likely bad
+    checksums) indicate some kind of problem with either the hardware or
+    the flight software. Display these to the user and do not erase the
+    flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 629a7637871b24fe6d1204aaa7185d84933d4639
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 19 01:04:19 2011 -0800
+
+    altosui: Always read whole eeprom block, even at end of flight
+    
+    Instead of stopping early, continue reading the whole eeprom block so
+    that the extra serial data doesn't end up confusing the next user of
+    the serial line, which may well be reading the next flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ca2cf1b7e03b8453b45b45e313a33ad65da9ad5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 19 01:02:00 2011 -0800
+
+    altosui: Mark empty eeprom records 'invalid', don't generate exception
+    
+    When reading empty eeprom records, mark them as 'invalid', but don't
+    generate an exception as it's normal to read these at the end of the
+    flight log.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 690feb166fd2bc6b6dfc26828f1efe9f5f1c6c0d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Feb 18 23:56:01 2011 -0700
+
+    update changelogs for Debian build
+
+commit 0630e7d6d8cf6abf0fe07f9a6df40ee472cce1ef
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Feb 18 19:54:18 2011 -0700
+
+    tie bringup scripts to Bdale's bench TeleDongle
+
+commit b080e933a65d268aaaec8cfd5f617a13d5babc43
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Feb 19 05:49:15 2011 +1000
+
+    src/ao_gps_skytraq.c: Update logging rate to 10Hz
+    
+    Send commands to skytraq to update baud rate to 57,600 bps, and
+    set NMEA output rate to 10Hz.
+
+commit aad7103dcf44e69a5a30e008836cce5542ea33e2
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Feb 19 04:17:17 2011 +1000
+
+    src/ao_gps_skytraq: simplify parsing code
+    
+    Added macros to make correctly constructing skytraq commands easier.
+    Simplified code path for NMEA processing marginally.
+
+commit fe5123fa801f5dafed8b052da607899d1ef20500
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Feb 2 19:12:57 2011 +1000
+
+    ao_radio: generalise setup of packet size
+
+commit 8b09cc1825645a57c256f38a2f9586ddecf6bda5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Feb 18 10:02:46 2011 -0800
+
+    altos/test: auto-configure acceleration parameters from the log file
+    
+    The flight test code had static accelerometer configuration values,
+    making it impossible to use data from different boards without
+    recompiling. As the eeprom and telem log files both contain the
+    necessary data, parse that instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d3bc27fabb6159ce58b14d0f7929b0f46f67c378
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Feb 18 09:54:01 2011 -0800
+
+    altos/test: Add dependencies in the Makefile for ao_flight_test
+    
+    Yes, it would be nice to automate dependency generation here, but I
+    can't be bothered.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c3080fdafff5212f267ba7c765a2f083435be799
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Feb 18 09:51:37 2011 -0800
+
+    ao-load: fix usage message to note that '=' is required for options
+    
+    The usage message was suggesting incorrect command line syntax; long
+    options use '=' between the option name and value, not whitespace.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a09501ab714c0638410d06f80903a8769d93c688
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jan 19 12:47:25 2011 -0700
+
+    update changelogs for Debian build
+
+commit 2bb83d90aa03d825ca1b751418c91b194b72f1f9
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jan 19 12:46:02 2011 -0700
+
+    elide changelog entries or re-release of 0.9
+
+commit 9541ccd9ff9e67e0862ca31706358d8308fc85d8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jan 19 12:42:40 2011 -0700
+
+    update turnon script to prefer TeleDongle as programmer
+
+commit 159fda30fe57349660c5e2d95017144ea3f5d7db
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jan 19 12:26:53 2011 -0700
+
+    update changelogs for Debian build
+
+commit 97f4f2e0d28eec1cf19d2d25140e42f6ac277700
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 19 11:21:52 2011 -0800
+
+    altos: Program default flight log max value for new boards
+    
+    New boards have no config space values at all, and so they need each
+    value to be set. Yes, this should be fixed so that there aren't two
+    copies of these assignments.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 480587cf514ba21885b24c3b8fcb98d6b76ea8f4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jan 19 00:05:25 2011 -0700
+
+    update changelogs for Debian build
+
+commit 6244f2316267738781e31a773b377bcf8c476918
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jan 19 00:04:45 2011 -0700
+
+    prepare to release
+
+commit 3c72103866f041107af49e01a5ccb6d4e6b6ac80
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jan 18 23:55:42 2011 -0700
+
+    update changelogs for Debian build
+
+commit 4ae724fe1d2ca0d712321c4fdc2200ff46d77428
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jan 18 23:54:36 2011 -0700
+
+    we need an install target to prevent parent dir make from failing
+
+commit ca7b549fbf62019b01a6e6c85da50645ea9a4502
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jan 18 23:48:08 2011 -0700
+
+    update changelogs for Debian build
+
+commit 9a5666f42d4d90a0a488fd0a85ae9914944fe0be
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 18 22:46:25 2011 -0800
+
+    doc: Build with 'make all' from top level. Build with 'make fat'
+    
+    This will make sure the docs are up-to-date for both regular and
+    fat builds.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 72a04d679d06aaad9c2b4297fefd585fc393ce2e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 18 22:39:07 2011 -0800
+
+    fat: Add docs to Linux package
+    
+    Oops. Missed this one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5d91c250179f44ca17c26fff36718b7026aa8ee0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 18 22:34:15 2011 -0800
+
+    fat: Add firmware for v1.1 and docs to mac/windows/linux installers
+    
+    We'll need to be sure to update this each time we add a product.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 26c4cc3054b1c7c9ed6ce3c2f21f6254b3245718
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jan 18 23:29:03 2011 -0700
+
+    freshen copyright year
+
+commit e2e20f6ce8a9c2bca36fde5730ccd7151377ec6f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jan 18 23:18:42 2011 -0700
+
+    add 0.9 revision entry, with caveat about telemetry format change
+
+commit 27e6dbbe95ae9b361d60576e0cbadb66792307f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 18 20:39:58 2011 -0800
+
+    doc: Add v0.9 features from altosui to documentation.
+    
+    New flight download UI and new config items.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 92d7841edcfc8a841f71f7f97cc541f8e55c4627
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 18 20:39:30 2011 -0800
+
+    doc: Don't delete telemetrum-outline.pdf
+    
+    This has a drilling template for the board.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c411dce69be58238b8312c2fd7405cbe8b5d4a5a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jan 18 17:27:11 2011 -0700
+
+    update changelogs for Debian build
+
+commit da42f406e88ccc821cd45d5a94d5afec65ec50e9
+Merge: ea4cdfb cf550f9
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Jan 17 09:50:17 2011 -0700
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit ea4cdfb87e03ecfb974f98305671265b6fb95372
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Jan 17 09:49:45 2011 -0700
+
+    update documentation to reflect reality that modifying a board or separate
+    pyro battery is not as simple as one trace cut on v1.0 and v1.1 boards
+
+commit cf550f9b96fa94d8db559e01df0e265bb1c7b572
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 16 23:23:45 2011 -0800
+
+    doc: Remove mention of ao_wake_task
+    
+    This has been removed from the altos sources, so remove it from the
+    docs too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4b71c4f4ed6cae23a7f4a2e7ae697da9ec614898
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 12:26:53 2011 -0800
+
+    altos: Use 5V reference data to correct accelerometer measurements.
+    
+    When the 3.3V and 5V values shift relative to each other (usually due
+    to changes in power consumption), the measured acceleration will
+    appear to shift. This patch converts the 3.3V referenced acceleration
+    value into a 5V referenced acceleration, eliminating this error.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2887fe7affc0706dbeb2f04df9a00a9b799903ed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 12:25:57 2011 -0800
+
+    altos: Optimize fetching of ADC data in flight code
+    
+    This stores the address of the desired sample in a local variable and
+    then fetches through that. Saves quite a few instructions.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2681a17500913cbaf3966f09380bb1d6b59e3863
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 12:18:32 2011 -0800
+
+    altos: Sample the accelerometer reference voltage on v1.1 boards
+    
+    This places the 5v reference samples in an array parallel to the basic
+    ADC values. It doesn't do anything with the values, just stores them.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 69290588980bb15732a99eca5c911a3b6e9a37b9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 12:12:02 2011 -0800
+
+    altos: Ensure flight code gets first crack at new ADC data
+    
+    Instead of having everyone wait on the raw ADC ring, have the flight
+    code wait on that and have everyone else wait for the flight code to
+    finish looking at the data and move its pointer forwards.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1b8d7313504240ed04e0747e9b0f6e9a83d323e2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 16 15:57:15 2011 -0800
+
+    altos: Auto-calibrate linux-based flight testing code
+    
+    Use the provided ground acceleration average to set the two
+    accelerometer calibration values so that the flight code will
+    detect pad/idle mode correctly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit afd3d3cdb8c2291c1c7cda7908392d68cd04f87f
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Jan 17 15:03:40 2011 +1300
+
+    Rework invalid accel cal detection code
+    
+    Slightly reduces code space.
+    
+    Uncalibrated accelerometer now enters invalid state as well.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 3566dee1cf83870396a0bb164f5549dd3faf58f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 16 14:40:31 2011 -0800
+
+    altosui: Remove spurious colons from eeprom selection headers
+    
+    This colons make the presentation a bit confusing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ab31b1c737d8fd32af482e5b06699f1b832a25a1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 00:56:22 2011 -0800
+
+    altos: Add DATA_TO_XDATA to linux test harness
+    
+    The flight test harness needs to expose every function used by the
+    flight code in some form, and this macro is about to become used.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit add2802a8a33336180fe6856241a7f4a8200e89c
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Jan 16 00:10:30 2011 +1300
+
+    altos: Added check for out of bounds accel
+    
+    Chose invalid flight mode instead of idle to give user feedback.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 58838c0b96a91da0bd0cd77c3ff312b589c08136
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sat Jan 15 23:21:26 2011 +1300
+
+    altos: Added check for an accel value above 1.5g
+    
+    When detecting flight or idle mode, this should indicate
+    that accel cal values are out of whack.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 118fe84c9ff1cc9d1653e67a2315e22e19d60a14
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 11:26:31 2011 -0800
+
+    altos: average 512 accel/baro samples at startup instead of 1000
+    
+    This lets us use a simple shift instead of a divide, saving a huge
+    amount of code space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8a775b8f9ecefa143050653d74dfd218b32b9bb5
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Jan 16 10:54:45 2011 +1000
+
+    altos: Restructure skytraq NMEA parsing code to save some space
+    
+    Splitting this into several smaller functions appears to make SDCC
+    generate better code.
+
+commit 7a35b2d7048669a96256d4ea0086299f8a0cb1df
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 22:42:38 2011 -0800
+
+    altos: Flush log when full
+    
+    When the log storage is full, make sure any pending writes are flushed
+    out so that the last bit isn't lost.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 47ee4597e55749e8f66f61a585ea32776979bf80
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 16:25:10 2011 -0800
+
+    altos: TELEMETRY PROTOCOL CHANGE. Switch to 16-bit serial numbers.
+    
+    What a terrible mistake! The flight computer serial numbers were
+    recorded in only 8 bits, so serial numbers > 255 would get truncated.
+    
+    There's really no fix other than bumping the field to 16 bits and
+    reflashing every TM and TD on the planet. Very unfortunate.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b22ba359a02297e39a446cbd5ef51e63b795624a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 12:05:50 2011 -0800
+
+    doc: inkscape tracks the filename inside the document
+    
+    telemetrum-outline.svg was renamed from telemetrum.svg and inkscape
+    appears to care.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d0a841b285fb398f0be72183ec3c9d1e358419a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 12:02:43 2011 -0800
+
+    altosui: Require 4 sats to light up the 'GPS locked' light.
+    
+    This tracks the same GPS signal requirement needed for 'GPS ready' and
+    ensures that we have a 3d fix.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1bfdce6fc3367fdf03e0dc7ddd94da18723b8ba3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 16 14:30:38 2011 -0800
+
+    altosui: Ensure serial device is closed after eeprom download finishes
+    
+    As this code is all event-driven, track which events will trigger
+    further work and block closing the device in those specific cases,
+    ensuring that all other code paths end up closing the device.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 987039b8f0b1d889aca9109d4c6a83f034ff64a7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 16 14:29:57 2011 -0800
+
+    altosui: Remove debug message when eeprom downloads are complete.
+    
+    This message isn't useful now that this code appears to work.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fb534aae15f0f1e5d69790e159d0287b6b8a514a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 16 14:28:35 2011 -0800
+
+    altosui: Use long input flush timeout when remote.
+    
+    100ms isn't long enough to capture pending remote serial input, so use
+    300 ms in that mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d4add23186b3586c99579d83efdc003f79e9bf7a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 16 14:26:18 2011 -0800
+
+    altosui: Make serial debug more complete and accurate
+    
+    Display all serial input, including telemetry.
+    Wait to display serial output until flush time, to debug missing flushing.
+    Show when devices are opened and closed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit deb3c7b9206be0c9c46f75d35c8f766c26d9838f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 15 22:43:37 2011 -0800
+
+    altosui: Reset eeprom download instance variables before reading flight
+    
+    To deal with downloading multiple flights in a single invocation, make
+    sure all relevant instance variables are set back to start of flight
+    download values each time a log is read.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eec9eb2e81535e62c52fbb2e57a2d33f88f92c1a
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Jan 17 07:56:53 2011 +1300
+
+    Close serial port if Download/Delete dialog is cancelled.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit df1c6ab3ac079199b5a12328c9ff5cfa6ac29b36
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Mon Jan 17 00:40:07 2011 +1300
+
+    Convert EepromSelect dialog to use a GridBag
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit fef302656f21ae0ab4772f72979cbb7f071da89a
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Sun Jan 16 20:25:19 2011 +1300
+
+    Re-order and re-arrange eeprom download dialog
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 35adb7c98fe02e84fff70c1bee22bfa019cfacc2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 14 21:44:59 2011 -0800
+
+    doc: Add telemetrum mounting template in svg and pdf forms
+    
+    telemetrum-outline.svg and telemetrum-outline.pdf
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fdba0f24afd59becc499d750bbdb80aea86066ac
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Jan 14 18:01:16 2011 -0700
+
+    update changelogs for Debian build
+
+commit 646e1926cdf56e2cf2425413ce716e6999b64a58
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Jan 14 18:01:00 2011 -0700
+
+    update changelogs for Debian build
+
+commit d908c2ebd0b11a54cfd922a192249d0f0df0ddb0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 14 16:47:36 2011 -0800
+
+    altosui: Add preference for serial debugging.
+    
+    This dumps serial input/output to stdout.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fcaee12a64d5e195b55b8f77c19dfc0c57ef5d58
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 14 16:47:19 2011 -0800
+
+    altosui: Ensure serial line is flushed after disabling remote link
+    
+    Flush the '~' character.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2d154be89246e111a36f7c2700effbb2c97da541
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 14 16:44:50 2011 -0800
+
+    altosui: Show dialog after successful delete or when no flights
+    
+    Make sure the user always sees confirmation for flight log management.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b490455807d1c70c81785ed8931a07ab44e8e421
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Jan 14 14:31:19 2011 -0700
+
+    update changelogs for Debian build
+
+commit 97b1ca994599ad262400eb3c685799582f7f1b94
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Jan 14 14:30:49 2011 -0700
+
+    update changelogs for Debian build
+
+commit d794ef9e0fbe4d13259db1bbd69f01717c14400b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 12 12:41:26 2011 -0800
+
+    altos: check for valid flight number in ao_log_delete
+    
+    Zero is not a valid flight number, and ao_log_flight uses that to
+    indicate 'no flight in this slot'. Check the user-provided input for
+    zero before looking through the slots.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8801b8c1947bd39f7c985b91a2ba8dbc81bcc91a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jan 12 12:40:45 2011 -0800
+
+    altosui: Add eeprom 'manage' ui to download and delete multiple flights
+    
+    This shows the list of available flights and provides options to
+    download and/or delete each one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 440d52e34364fdeeddc76a2d744cc6d1c934364f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 11 21:28:07 2011 -0800
+
+    altosui: Add support for parsing list of flights from the 'l' command
+    
+    This adds parsing support to enumerate the available flights, but does
+    not yet provide any UI to use it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ad419d81c90ef6a16656970466313767fef830f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 11 21:26:41 2011 -0800
+
+    altos: oops -- 'e' command was only showing 7 of the 8 bytes per line
+    
+    Just a silly off-by-one error when printing out the data received from flash.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bd2480fd757b67557d9c7de42e402034002c3e37
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jan 11 15:39:24 2011 -0800
+
+    altosui: Split eeprom download code apart
+    
+    Create separate 'download config data', 'read single record' and 'read
+    block' functions. This code will be shared with future multi-log
+    reading code for new firmware.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c437b14b7fc7afdfc7b809a04d7fa29d5e742307
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Jan 7 21:00:10 2011 -0800
+
+    altos: Remove redundant initialization of ao_interval variables
+    
+    These are all initialized in the ao_flight_drogue state transition.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 57de960b8148bf485607898c3d66af6994d76481
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Jan 7 20:52:33 2011 -0800
+
+    altos: Remove unused accel_vel_mach and accel_vel_boost variables
+    
+    Presumably left-over debugging code.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca66f86a899c191b6362a334417fc84a79349677
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 20:46:29 2011 -0800
+
+    altosui: Add configuration of flight log size
+    
+    This adds to the TeleMetrum configuration UI the ability to set the
+    maximum flight log size.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 52ac83fedbfd380d14d4df2e79992bbdfba3552a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 20:26:39 2011 -0800
+
+    altos: Check for full log and complain
+    
+    Reports special tone along with the continuity checks.
+    Reports flight 0 in telemetry.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 00891b40754962ef6530b237ef52017bb58112d5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 20:25:09 2011 -0800
+
+    altos: Check requested log max size against available space
+    
+    Make sure the amount of memory requested for a single log isn't more
+    than is available on the device.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bbddcae2a15b6d430e84956ddf26955aa9173cc0
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Jan 7 20:18:25 2011 -0800
+
+    altos: Optimize Morse code generation
+    
+    This reduces the size of data and code needed to report the
+    flight states
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 73620c41017c1774d6190dfd4be5b742eb64f8d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 20:13:28 2011 -0800
+
+    altos: ensure erase mark is written when erasing flights
+    
+    It was getting called only when *failing* to erase a flight (oops),
+    secondly, it wasn't getting written because ao_storage_flush wasn't
+    getting called.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit edd22ee49adf60c35f2fe6ba97c111b7ad4131c2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 17:56:06 2011 -0800
+
+    altos: report flight log offsets in hex block numbers instead of bytes
+    
+    makes them compatible with the 'e' command.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1cc08af4f4a1ff61fc0deca3bdd95e8a5ca8ec5c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 17:55:54 2011 -0800
+
+    altos: white space fix
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d8c9684239b67a69a4a3d24202a6df0d73c4f7f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 17:54:54 2011 -0800
+
+    altos: Speed up at45 and 25lc erase speeds
+    
+    No need to read the block to be erased before erasing it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2722703bd848b07a02d3ce0c83a502eca52a9f1d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 14:58:39 2011 -0800
+
+    altos: support storage of multiple flights.
+    
+    This adds the logging support for dealing with multiple flights
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9b210bc33cd95e7108ab51925fdf0d5e8deaf7e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jan 7 10:05:11 2011 -0800
+
+    altos: Add configuration parameter for maximum flight log size
+    
+    This parameter will permit available storage to be split into multiple
+    separate flight logs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3bcf14f12f6681888c3162d0f33ef2d454eb75dd
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Jan 7 00:52:50 2011 -0700
+
+    update changelogs for Debian build
+
+commit 408a3e04362d626778640dd0ce7d356d3863de53
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 6 23:38:13 2011 -0800
+
+    altos: Mark end of available flight list with 'done' to make the UI
+    code easier to write
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e991f52276248fa08d96fbecc458bc478e98d299
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 6 23:35:31 2011 -0800
+
+    altos: Rip out 'optimization' in ao_log_scan
+    
+    Remove premature optimization to avoid re-scanning the flight logs for
+    the best empty entry.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 569a1dac55b70c30f01afa7bcb74442ecdd85d85
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 6 22:37:38 2011 -0800
+
+    altos: Move common storage code to ao_storage.c. Add M25P80 driver
+    
+    This reworks the storage API so that you erase blocks and then store
+    data to them so that the M25P80 driver will work.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e4ba9bf4291bf17c777c8c3ef7c71e4a30b9947a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 6 17:34:58 2011 -0800
+
+    altos: Require manual flight erasing.
+    
+    This supports flash chips that require larger erase blocks.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ddcc94da4326f9ce954bd31a46b36165c58e6c18
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 6 12:51:39 2011 -0800
+
+    altos: Simplify storage API
+    
+    This removes the config-specific APIs and exposes global variables for
+    the available storage space, block size and config storage location.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3f1b1488bdc92fa9277dc549ba9f3210a8d4c8c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jan 6 11:28:35 2011 -0800
+
+    altos: packet and usb i/o routines use 'char', not 'uint8_t'
+    
+    Just fixing the type of a local variable holding a character
+    passed from the packet link to usb.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5688af4e4d7ca8e559d758257e79b7979f1cc924
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Dec 22 21:13:56 2010 -0800
+
+    altos: Add telemetrum-v1.1 directory
+    
+    This just clones the v1.0 build; changes will be added on top of this.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9f7296b3feab872bf51fc369ade69cc1e7cf7a3f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Dec 22 21:06:22 2010 -0800
+
+    altos: Split out SPI driver.
+    
+    For TM with the companion connector, the SPI bus will be shared among
+    multiple devices. Split out the existing SPI code into a common
+    driver, with the SPI bus protected by a mutex.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 51c410c1c952e0e9bcf1b2c438813de63753be5f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 28 00:24:54 2010 -0800
+
+    windows: Update NSIS installer file to use compatibility IDs
+    
+    This allows the file to contain a single InitDriverSetup function,
+    making things shorter and (I hope) clearer.
+
+commit 4a42f9d5c20dcaf5fd7591ccf9e32c6130d9d538
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 28 00:22:14 2010 -0800
+
+    windows: Add compatibility IDs to telemetrum.inf
+    
+    This will allow the .nsi file to reference just the compatibility ID
+    instead of needing to have all of the USB IDs listed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 484b44e81b655f1ecb48256095382a56d2839bae
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 26 17:39:40 2010 -0800
+
+    altos: eliminate ao_wake_task
+    
+    Waking up a task waiting on some random object is a bad idea. Fix
+    the waiters to look for suitable signalling.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 07213dc34fa20470a4b36a327a83d75b0f010ebb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 26 16:14:15 2010 -0800
+
+    altos: clean up radio abort paths. Share radio code.
+    
+    Instead of aborting the DMA and radio operation and expecting that to
+    be handled reasonably by the radio receiving task, rewrite things so
+    that the abort function just wakes the receiving task while that
+    terminates the DMA and cleans up the radio.
+    
+    This eliminates all kinds of nasty bugs dealing with radio abort
+    smashing the radio registers at the wrong time, or interrupting a
+    radio transmission.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b62580855c5144f5bc7e0172289bce08814d9472
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Tue Dec 14 03:40:18 2010 +1000
+
+    altosui: move maps to subdir, fix E/W mismatch
+
+commit b8d2eb5509096fd5bd04598b1312077659109620
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Dec 1 00:15:17 2010 -0700
+
+    update changelogs for Debian build
+
+commit ea95c060f8bd959cefc60dd6d411151f3c91384e
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Dec 1 00:12:05 2010 -0700
+
+    fix symlink paths in rules file
+
+commit f95220b6f0cc74928e0e7d7c7e1f443eafc32a02
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Dec 1 00:03:23 2010 -0700
+
+    update changelogs for Debian build
+
+commit ea7130e953622884afc348265f5a4c58f9876823
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Dec 1 00:00:58 2010 -0700
+
+    modify Debian package build to deliver one copy of background.png for
+    the themes and symlink it into place for gdm and slim
+
+commit 3696b30a8d1eb351353d84b5f7af8eeff55c468b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 29 21:47:13 2010 -0700
+
+    update changelogs for Debian build
+
+commit 11b3f9ff715017a2dec02003275885334f22c009
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 29 21:46:02 2010 -0700
+
+    releasing 0.8.1
+
+commit 5f3f11e11dff9b9f4d1dde279c0d474de0de12a5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 29 20:41:49 2010 -0800
+
+    Add minimal release testing plan to Releasing
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f0a4deee23984a8f779917bbeaf74a66a0abf592
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 29 20:34:27 2010 -0800
+
+    Move "Releasing" to top level
+    
+    This file documents the AltOS release process for all systems.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b727156ef0a7fb6e442ca28be27eb344a213ecf8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 29 20:17:35 2010 -0800
+
+    windows: Add jfreechart.jar and jcommon.jar to windows install image
+    
+    These are necessary for the Graph Data button to do anything useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3e2220a180f95971d222a597d2057ca328c27356
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 29 21:29:14 2010 -0700
+
+    adding Bdale's release process document to the source tree
+
+commit 61a924099800494b589cbbb87c65b552ccbd8394
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 29 14:40:27 2010 -0700
+
+    fix an Altos vs Altus typo in the docs
+
+commit e840b6594b8a939f148fa7231e1b06a280d94074
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 22:42:43 2010 -0700
+
+    fix section layering
+
+commit f39698bbc12afdfadfac56c90030e16db93cf4fc
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 19:46:03 2010 -0700
+
+    fix publish target in doc/Makefile
+
+commit 13cea7a96821165a10a8b2433af1da7508882b0a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 18:48:31 2010 -0700
+
+    moved doc dir in web content to AltOS tree
+
+commit 6f3c72462af86a7d9dec20f4c813a524a5be9fbb
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 18:32:17 2010 -0700
+
+    update changelogs for Debian build
+
+commit a375942979dbcd8239d8c0addb10616e6048f6ea
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 18:31:26 2010 -0700
+
+    rewind changelog to recover from stupid build failure
+
+commit 263cf6c4fc5c1a240d719bb0ceb33393864fb3d6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 18:24:00 2010 -0700
+
+    update changelogs for Debian build
+
+commit 2615289265b6f8fa08827be794b4eee569fc6333
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 18:23:17 2010 -0700
+
+    update changelogs for Debian build
+
+commit 5e23bcc2d2ad33a839fedd2158213098a05cd9ae
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 28 18:21:31 2010 -0700
+
+    declaring 0.8 released
+
+commit b09d3e11c32abd07f8e11b483ac4b57148f4f2d8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 27 23:08:41 2010 -0800
+
+    windows: Bump .inf file version
+
+commit 42c2394ccc1a1ee6dc134ed963a4f79acf031d0b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 27 23:04:50 2010 -0800
+
+    windows: more .inf file hacking
+    
+    Add a LayoutFile reference
+    Add the FakeModemCopyFileSection
+    
+    Copy AltosMetrum.Install section to AltusMetrum.Install.NT section. I
+    don't know what this might do; existing files seem inconsistent.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 26369a92398f4ebe3fcd54ce34cebffae0cfdf07
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 27 22:39:31 2010 -0800
+
+    windows: remove some non-existent .inf file section references
+    
+    The mfglt and VerboseResultCodes sections are not present in the .inf
+    file, so remove refernces to them.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 641c5373724d34c3adfcf42420a528d6bba736b9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 27 22:35:18 2010 -0800
+
+    windows: try harder to get windows install to work
+    
+    Add devIDs to .nsi file. Fix install section name mapping from the
+    hot-plug info.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cd414e2c04ce5ecbc75f19325a6d6f82cd489fb3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 27 16:30:29 2010 -0800
+
+    altosui: Correct windows hardware IDs for nsis installer file
+    
+    Need real hardware IDs (encoded USB ids) to get windows to
+    auto-install the driver?
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f834b22905db87557f729f942607dc363b013694
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 25 22:31:33 2010 -0700
+
+    update changelogs for Debian build
+
+commit da3b39d6b1b5ba48686c2a4add4b5448fd5711af
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 25 22:29:01 2010 -0700
+
+    changed main document name from telemetrum-doc to altusmetrum
+
+commit ab6c9c983a2830bc8807e1b75d2576141b73632d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 25 19:30:02 2010 -0700
+
+    update fat target in Makefiles to reflect move of altosui and libaltos
+
+commit b8f05cdc0e9b4a96852eed9d38ff6d5950e2d2ed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 25 17:29:28 2010 -0800
+
+    altosui: Clean up flash code to ensure swing gets called from right thread
+    
+    This moves all of the flash code to a separate thread and passes
+    messages back to the swing thread to keep the UI up to date.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit adbb14c63d85b7a54223f88ac623571456f4a462
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 25 16:28:04 2010 -0800
+
+    altosui: Remove gratuitous threading from device flashing UI
+    
+    There's no need for a thread here, and swing doesn't want us to use
+    one anyways.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f88520089660845009148b69bfcea6c9dff9672
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 25 16:23:18 2010 -0800
+
+    altosui: Flight data download GUI operations called only from main thread
+    
+    Swing doesn't like UI functions being called from non-dispatch thread,
+    so fix up the eeprom download code to use SwingUtilities.invokeLater
+    to make sure this works right.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6d3612e267cd4c1e7fdd74fc33952b3f26f870f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 25 16:09:37 2010 -0800
+
+    altosui: Eliminate unnecessary thread from config UI
+    
+    There's no reason to use a thread to run a dialog box, and
+    swing doesn't like threads anyways.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 12fb7f0e70cd244475d84469f93283112478d1e1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 25 15:56:42 2010 -0800
+
+    altosui: Only call swing display functions from main thread.
+    
+    Swing insists that all display functions be called from a single
+    thread, and the flight window wasn't following this for display
+    updates. Use SwingUtilities.invokeLater to make sure the flight UI
+    updates happen in the right context.
+    
+    Fixes a UI freeze on Mac OS.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b74cec6cd0bc043f53e9e28472765aa343136813
+Author: Bob Finch <w9ya@qrparci.net>
+Date:   Thu Nov 25 13:30:48 2010 -0700
+
+    Updated AltOS PKGBUILD to track makefile changes
+
+commit f88bde21d76a4ff91099a5051153ebace1619978
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 25 11:16:55 2010 -0800
+
+    altosui: Hack up standalone makefile to maybe build altosui again
+    
+    This isn't tested, but at least the paths are more likely to be correct
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3d98440d53378aaa6da87ed65e9abb2f96f7ee49
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 25 11:16:28 2010 -0800
+
+    altosui: Make windows bits build after moving altosui directory
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4893ed50bc14772986ac02f9b39928f1882da923
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 23:54:08 2010 -0800
+
+    Revert "altos: Don't abort radio transmissions with ao_radio_abort"
+    
+    This reverts commit 54468e5dc567aaac5c5c20e921859b7cec28bb88.
+    
+    With this patch in place, TD could not be placed in 'packet' mode.
+
+commit cb08bc264c71ca972027392b42f347a03df76a43
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 22:55:08 2010 -0800
+
+    doc: Rename telemetrum-doc as altusmetrum
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 554bdd25e132dbaec322bc11f94093d2c2e78751
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 22:49:33 2010 -0800
+
+    doc: Add more authors, fix URL formatting, note that AltosUI actually exists
+    
+    Add aj and bfinch as authors. Insert an acknowledgements section. Fill
+    in the Fire Igniter section in the AltosUI chapter. Then change the
+    section talking about the future plans for Java to mention that they
+    actually exist now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bcf78b67717374b5971820021b83061e2e9734cf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 21:39:18 2010 -0800
+
+    doc: Reformat altos to use sections for each function
+    
+    This places them in the TOC, making them easier to find.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 51c7741040d95c5deece939dae5e4136cc04afc4
+Merge: d1dbe3b 4e47c44
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 21:00:52 2010 -0800
+
+    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>
+
+commit 4e47c44d335276cf0dc5ed3a0756e50c98c1b9b9
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Nov 24 21:44:53 2010 -0700
+
+    manually fold in documentation work from the master branch
+
+commit 8a68c1da253c0b29a7cb9c7540c20585ad6e3dec
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Nov 24 21:21:53 2010 -0700
+
+    tweak rev history
+
+commit 7cd1c7765d137df711caeeb69abaaba1b36e0a65
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Nov 24 20:53:36 2010 -0700
+
+    fix missing section close in Site Map content
+
+commit db2b19b8f0d452d682d53c7ed0ff6e359b46efa0
+Merge: b372f3c 915f881
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 18:57:35 2010 -0800
+
+    Merge remote branch 'aj/buttonbox' into buttonbox
+
+commit b372f3c0ee4ec49aabe61c169cb1eb9bb4fb2cfc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 18:50:46 2010 -0800
+
+    Missing change to top level Makefile to build altosui
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 915f881d61294dc6f5a6a3e8d75567e18492a631
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 25 09:52:30 2010 +1000
+
+    doc: Document altosui "Site Map" tab
+
+commit f01096c4b42f9a4720ed0414826c2a283a992545
+Merge: 357826a 3fbefb3
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 25 09:10:50 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit 3fbefb3eea981d34a09496cf8abf0119de2e35bf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 14:57:57 2010 -0800
+
+    Move altosui to the top level, placing libaltos inside it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 357826aa9c7b42c59f5d52b8eb016d73b6da0c7f
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 25 09:07:34 2010 +1000
+
+    docs: Document altosui "Graph Data" button
+
+commit 7811e6dfa6caf10251da7df7c24b98cdc3787892
+Merge: 71b1949 7a50837
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 25 08:47:36 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit 7a50837ea0d92db3f469f197ec8210aee22aa143
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 24 10:55:18 2010 -0800
+
+    altosui: Make sure packet mode is turned off when the connection fails
+    
+    When the packet connection times out, turn packet mode off when
+    closing the serial port.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7d90e2f6009e060fb59c519f7e564483a7ca6872
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 23 20:17:44 2010 -0800
+
+    altosui: Let people fire igniters that don't read as 'ready'
+    
+    This provides for igniter testing with LEDs or other materials that
+    don't look like regular igniters.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3233985a132e1d660e6df12d0056b6729f16faf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 23 19:09:31 2010 -0800
+
+    altosui: Disable radio configation over packet link.
+    
+    Attempting to configure the radio over the packet link will only end
+    up confusing the user, so disable it. This also works around a bug in
+    older TM code which would lock up when trying to do this.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6cd9be22f06f21d12ee2f668989d83d3c61d14c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 23 19:08:07 2010 -0800
+
+    altosui: New AltosSerial.set_radio function sets channel/call
+    
+    Use this anytime you need to set the device radio channel and call
+    sign, either for telemetry reception or packet mode origination. This
+    uses the saved callsign and per-device radio channel number. Do not
+    use this when opening a telemetrum as there won't be a saved channel number.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ed7cf7d262fcf7c0c677c2fb981582b571de9e5e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 23 19:04:55 2010 -0800
+
+    altosui: Make AltosSerial.flush_input keep reading while non-empty
+    
+    Flushing the input buffer can take a while, especially over the packet
+    link. Keep reading while stuff is appearing on the reply queue.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3b9db8c82d26a6a2e43d4ca40742fc1bdc502380
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 23 19:02:54 2010 -0800
+
+    altos: Make radio test command careful with the radio mutex.
+    
+    Remember whether the radio test mode is on or off and don't try to do
+    either of them twice to prevent the mutex from being acquired or
+    released twice.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 54468e5dc567aaac5c5c20e921859b7cec28bb88
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 23 18:57:49 2010 -0800
+
+    altos: Don't abort radio transmissions with ao_radio_abort
+    
+    We only want to abort pending radio reception to release the radio for
+    other use, or to change the radio channel. Let radio transmission
+    proceed. This fixes a problem with using packet mode to configure the
+    radio channel; if the packet transmission is aborted, the TM ends up
+    wedged.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d873dc28f0752aeb58a6263e42bdd5b9095bd392
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 23 18:56:46 2010 -0800
+
+    altos: remove unused variable from ao_igniter
+    
+    The 'status' variable used to hold a reported status value from the
+    igniter after firing, but we ignore that now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 71b1949e50f4533bcf44537da65b19bc67863c8e
+Merge: a79225c f1892b1
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Nov 24 12:14:11 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit f1892b137b1de3d6caf0293bd40ed5c3e4948066
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Nov 23 18:58:11 2010 -0700
+
+    lose the placeholder on how GPS works, as it's going to be a
+    while before I tackle that, if ever.
+
+commit a79225c215f17fa5218ddd9db4fc3f5c563a9f74
+Merge: 84cd5d4 853b711
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Nov 24 11:55:14 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit 853b7112e34212040c4cb7289f9cfdb2f3ea9f90
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Nov 23 18:53:18 2010 -0700
+
+    merge Keith's AltosUI documention into "the big book"
+
+commit 84cd5d42d8b5659463544fe2a400758b56478609
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Nov 24 02:13:32 2010 +1000
+
+    altosui: sitemap uses rocket gps if no pad gps
+
+commit c7119c21baa9d4ca681975b8613ade6593f65577
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Nov 24 02:11:36 2010 +1000
+
+    altosui: don't switch away from user selected tab
+
+commit ae55a107f12546dc65f04618c7abc17beb920d73
+Merge: d1005f6 737f2fd
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Nov 24 01:53:46 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit 737f2fdd012202f453120ece117ae5e859b32082
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 22 22:26:19 2010 -0800
+
+    doc: Add internal documentation for AltOS
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5523e7d55ecc8d310e495fa4f5115f7483c42d65
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 22 21:07:10 2010 -0700
+
+    add a rudimentary --help for command line use
+
+commit d1005f68376d695039c314b8d7a68bbf9acbca4f
+Merge: 9a83e0d 22c0978
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Tue Nov 23 10:14:55 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit 22c09781af1df4b38562b577e9926c23e4a397f2
+Merge: b27327a a79606a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 22 16:02:22 2010 -0800
+
+    Merge remote branch 'origin/buttonbox' into buttonbox
+
+commit b27327a05d249eaf969b67d2a8d12fc6a93841f0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 22 15:56:04 2010 -0800
+
+    altos: assume igniter worked.
+    
+    Many igniters don't go open when fired, so there's no way
+    to know if they worked. Assume they did as a failed igniter is
+    unlikely to do anything when fired again anyways.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 68323cbb222f1f33198a42abaa0550af22f75a93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 22 15:53:27 2010 -0800
+
+    altosui: Close serial port when debug link fails
+    
+    If the debug connection isn't working, close down the serial port when
+    reporting the failure.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9a83e0dc79f7a7467c7814d58daa2a2b89e50972
+Merge: 902735f a79606a
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Tue Nov 23 08:07:04 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit a79606a6507fc01a74910f7959e84c4e9a730714
+Author: Bob Finch <w9ya@qrparci.net>
+Date:   Mon Nov 22 12:24:42 2010 -0700
+
+    Added PKGBUILDs for deps into contribs
+
+commit 377ee7e90ecd028f984cd1abce96b2efc3b5b977
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 21 14:03:17 2010 -0800
+
+    altos: Add on/off modes to 'C' command
+    
+    This lets the user turn the radio on/off and then invoke other commands.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 902735ffbfdd97672d52b09f17cdcd619193fd05
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Mon Nov 22 05:29:26 2010 +1000
+
+    altosui: keep sitemap more centred on rocket
+
+commit b85df38b5611e45cb9296df07b720badf74ac26e
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Mon Nov 22 05:22:17 2010 +1000
+
+    altosui: improve sitemap scrolling behaviour
+
+commit 4a9ded5b39ed08e13abc2cddba8b712f62b983f2
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 17:39:50 2010 +1000
+
+    AltosSiteMap: ensure buffer around active tile
+
+commit ec47bc93a487614714a752cb30ec9fe3d8f72929
+Merge: 0393830 e7954c8
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 16:08:37 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit e7954c820763f80e993f9f822e837725cf36af84
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 22:03:26 2010 -0800
+
+    altosui: When fixing eeprom gps time information, make GPS data valid
+    
+    Eeprom files may be missing the GPS time (due to a firmware bug). Working
+    around this involves finding the next valid GPS time and using that to
+    create a fake GPS time entry. However, that next GPS time may not
+    be locked or may have few sats as it is from the boost stage of the
+    flight. Fix this by simply forcing the fake time packet to have
+    4 sats and be locked.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0393830f85da5efc96bbdf0d9769b66019c34b33
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 15:13:35 2010 +1000
+
+    AltosSiteMap: limit nr of tiles to 200x200
+
+commit a08b2a6363c194195db92029743f6612676373ce
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 15:03:21 2010 +1000
+
+    AltosSiteMap: never accept 0,0 as lat/long
+
+commit 84e570d8a8a52e0d358582135ec1b3a12be94c26
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 14:45:10 2010 +1000
+
+    AltosSiteMap: refactor tile collection
+
+commit 71e487344395a8efc9cd279aad92f601ff4c6d3d
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 14:05:00 2010 +1000
+
+    AltosSiteMap: thread safe tile addition
+
+commit c040bcd06679484175542208fb564d0271a7fc1b
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 13:19:36 2010 +1000
+
+    AltosSiteMap: try to get new tile construction right
+
+commit c3994dd82d489289ebc99ff9c5fa88f560c023ac
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 13:07:11 2010 +1000
+
+    AltosSiteMap: extend map if rocket goes far away
+
+commit 835b903727a2eabda8d9659cc46e53301f92897c
+Merge: 440a0f3 8789135
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 11:15:02 2010 +1000
+
+    Merge branch 'sitemap' into buttonbox
+    
+    Conflicts:
+       ao-tools/altosui/AltosSiteMap.java
+
+commit 878913551a1e4e3c8f2b39fa4aeb234880735a1c
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 10:55:22 2010 +1000
+
+    AltosSiteMap: explain tile size better
+
+commit 440a0f3f5130eb0c8e614691892be8c94e7fd3c3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 16:55:12 2010 -0800
+
+    altosui: Set site map flight path lines to 6 pixels anti-aliased.
+    
+    Much more visible over the map.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2a7dc3ba36bac81640a9498e0d0caf1470b57c19
+Merge: e5b1ada ece2c86
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 10:45:15 2010 +1000
+
+    Merge branch 'buttonbox' into sitemap
+    
+    Conflicts:
+       ao-tools/altosui/AltosFlightUI.java
+
+commit 8df185cd95cfecbed8272dd1275d077c5b45535b
+Merge: ece2c86 1e71264
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 16:35:48 2010 -0800
+
+    Merge remote branch 'aj/sitemap' into buttonbox
+    
+    Conflicts:
+       ao-tools/altosui/AltosFlightUI.java
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e5b1adae9b23b98a6321986f5cd67c9d3166b87f
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 10:34:39 2010 +1000
+
+    AltosSiteMap: better gps check, lower zoom
+
+commit ece2c86e2641b2cd613791293526c492b1606aa1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 16:19:42 2010 -0800
+
+    altosui: Rewrite info table to mix with scroll pane well. Fix startup size
+    
+    Using a single table for the info table means that the scroll pane
+    automatically picks up the table headers and shows them above the
+    scrollable view.
+    
+    This patch also fixes the application size at startup so that no
+    scrollbar is required in the info table, and the window is < 800x600.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1e712647dd6df1e77650db705f3ac32a3c8f6907
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 08:58:44 2010 +1000
+
+    altosui: reindent
+
+commit a59a204e188e40ec8848a0dc63d6de710cee3039
+Merge: 8263630 37f0201
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 08:56:13 2010 +1000
+
+    Merge branch 'buttonbox' into sitemap
+
+commit 82636305021c41d676f5f0f11378724fe0de0079
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 08:44:13 2010 +1000
+
+    AltosSiteMap: be more polite about preferred size
+
+commit 37f0201d724693528f37ac7d275f68f90cf94da0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 14:31:23 2010 -0800
+
+    altosui: change descent tab formatting to four columns
+    
+    This places labels to the left of each field. For igniter voltages,
+    it uses three columns for the labels.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 66ebd954d9c9a44a8db0ee713c682e39306fabd8
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 08:28:24 2010 +1000
+
+    Add GrabNDrag.java
+
+commit 72f5e05f9f0055f2cef8b840812f090556c94338
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 08:18:39 2010 +1000
+
+    AltosSiteMap: major refactoring
+
+commit b47517d4c2e49f6f7b9954d2c85f96397fe1103e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 14:06:37 2010 -0800
+
+    altosui: re-indent
+
+commit 6f8bc2ad20b715343e0510563ab0f14787ef3e07
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 01:34:52 2010 +1000
+
+    AltosDescent: switch elev from height to range
+
+commit 89f44c5587ea4f927d5e398b6af919df0d6561c3
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 01:27:01 2010 +1000
+
+    AltosAscent/Descent: tidy up layout
+
+commit 51e403145d28ac913e36d205077a613845596be2
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 00:17:51 2010 +1000
+
+    AltosSiteMapTile: draw boost circle as well as landed
+
+commit 25ffe1cc7823895886b4777f310b4bda1c80133b
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 21 00:07:16 2010 +1000
+
+    AltosSiteMap: automatic fetching of map data
+
+commit 20f714bbe3137de8fb7491b39985021fd1774930
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Nov 20 22:49:51 2010 +1000
+
+    AltosSiteMapTile: seperate map and drawing layers
+
+commit 58f8d069ce9488e2987b8e92caa69fe68cda7569
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Nov 20 21:06:37 2010 +1000
+
+    AltosSiteMap: add autoscroll and grabndrag scroll
+
+commit 74cab8503b51ba6fb05a4d12a031c749e870b0ef
+Merge: 0ecf033 9a99cab
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Nov 20 18:20:45 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit 0ecf033110084f1a8be98282d7029dc14f70dab5
+Merge: 081fbd5 71c41ea
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Nov 20 18:14:30 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit 9a99cabc1c34c657fc95246192ba6d330f5f22d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 00:13:58 2010 -0800
+
+    altosui: Fix channel changing in flight UI to actually work
+    
+    Replacing the menu with a combo box required reworking the way events
+    are delivered from that widget back to the channel changing
+    function. Just delete the old magic and use the JComboBox action
+    listener directly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 71c41eadd12c3ece5fffce7669e4991778046d4e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 20 00:09:03 2010 -0800
+
+    altosui: Initialize display thread state in constructor instead of run
+    
+    Some state will get set before run is called, initializing it there
+    can be too late.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 081fbd5715f9d3d81d98e149fb95d40447c07a79
+Merge: 90b9bc4 7920ed5
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Nov 20 17:40:49 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+    
+    Conflicts:
+       ao-tools/altosui/AltosFlightUI.java
+
+commit 7920ed5c34b088f45ce4213b061ddd1ffe22cee8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 23:18:51 2010 -0800
+
+    altosui: calling thread.interrupt with null thread doesn't work well
+    
+    This was a left-over from debugging the previous patch.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8c8dc3794c7b5fa5a5b43b1c461d6c8bb3ab425d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 23:09:15 2010 -0800
+
+    altosui: When switching log files, don't terminate log thread
+    
+    The log thread automatically switches output files when the incoming
+    telemetry changes. Don't use 'close' for that as 'close' terminates
+    the log thread as well as closing the file. Create a new
+    'close_log_file' function which just closes the file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 594e80572821f1848db062d0cff18ca8bf0d90ce
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 22:44:48 2010 -0800
+
+    altosui: switch channel selector to combo box. Shorten displayed device names
+    
+    A combo box displays the current value, which is quite nice to
+    have. Add a 'toShortString' for AltosDevice so that the window frames
+    and error messages don't have extra spaces generated by the
+    altos_device toString method.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fa07afc73bc5eccff8464a2def05ad600da33c97
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Nov 19 23:33:42 2010 -0700
+
+    update turnon scripts to use stashed copies of stable release firmware
+
+commit 9ffc2eb53a47e435f39b02896b0e43ae5f47f450
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 18:25:48 2010 -0800
+
+    altosui: Use timeouts to recover from broken packet links.
+    
+    This puts timeouts every place the system reads from the packet link
+    and aborts the in-progress operation if it takes more than a second to
+    get a response.
+    
+    Also mixed in here are persistent igniter status displays for the
+    ejection testing UI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 68078eab3c07d8dc83302747cf6f3dcb1797c6ce
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 20:44:29 2010 +0800
+
+    doc: Document the 'Flash Image' operation.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b4bdda65488e8ef27d2889cb6cc8eda3c5d50e0a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 20:29:14 2010 +0800
+
+    doc: git ignore generated doc files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f0542085de2139ef562af068ec05fa73f47c73b1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 20:26:49 2010 +0800
+
+    doc: Add preliminary altosui documentation
+    
+    Also, update the Makefile to allow for further documents to be added
+    without a lot of custom rules.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0e7a10f71803d60f8b34c5a91efd220449442769
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 17:16:03 2010 +0800
+
+    altosui: Clean up global AltosUI configuration settings dialog
+    
+    This dialog had a mish-mash of styles and was confusing.
+    Now it's got a label for each line, and suitable setters for
+    each element
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8f72f08839346fb225238420324f0edcd070e531
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 17:14:17 2010 +0800
+
+    altosui: Unify datafile selection to AltosDataChooser
+    
+    Instead of having several separate intefaces, use a single dialog for
+    selecting data files for graph/export/replay.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 24ffcf86c43290ce0f70fb4ee0984b3debdb8a5f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 19 15:41:30 2010 +0800
+
+    altosui: Add igniter ground testing code
+    
+    Not yet hooked up, but the UI is finished.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 90b9bc4475011bead7117ed72fa5efa0f77b2813
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 19 13:30:00 2010 +1000
+
+    AltosSiteMapTile: adjust scale to 1 nmi per tile
+
+commit fa45336062523838ba8abb08427cdc4d9c7de7a8
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 19 13:17:29 2010 +1000
+
+    AltosSiteMapTile: adjust centering calculation
+
+commit fda93afcd8aa4133b0e5f008b824d072e338d0ed
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 19 13:02:05 2010 +1000
+
+    AltosSiteMapTile: autoscale to about 2 nmi per tile
+
+commit 483346a03c94b200692f5e6d59f3feee4dcf2ace
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 19 12:09:46 2010 +1000
+
+    altosui: tile site maps
+
+commit 939be6793238a275b7682ecc376fed14379cf044
+Merge: e68fe94 1a4b6e9
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 18 05:54:06 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+    
+    Conflicts:
+       ao-tools/altosui/AltosFlightUI.java
+
+commit 1a4b6e96f823035b113f01d1bdfd61afc1f33e25
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 16 22:46:29 2010 +0800
+
+    altosui: Add igniter status to ascent and descent tabs
+    
+    Monitor igniters during all phases of the flight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d0eb41619544ead6d9dab3a8d024a12936c9cdd0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 16 22:20:00 2010 +0800
+
+    altosui: Cleanup flight UI layout
+    
+    Use common constants for fonts and insets
+    Shrink fonts so that the window is < 600 pixels tall.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fcca333cda64be35f0c9fb0109eef1be3709dddd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 16 21:49:59 2010 +0800
+
+    altosui: Add callsign configuration in AltosUI configuration dialog
+    
+    This callsign is used during packet communication.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d1dbe3b69e6f95ef8ecd4cf959863b922ab47c66
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 15 11:08:00 2010 -0700
+
+    add a paragraph about forcing TM back to idle mode if an accel cal goes badly
+
+commit 39e371561469d8e5059638ffa4e7075f391de268
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 15 23:14:51 2010 +0800
+
+    altosui: add reboot button to telemetrum configuration UI
+    
+    This lets you reconfigure and reboot telemetrum, including over the
+    radio link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 257e97137325f5dbbd6aa034f20fd6937b67df90
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 15 22:38:35 2010 +0800
+
+    altosui: eliminate menu bar, moving elements to buttons.
+    
+    This adds a new 'configure AltosUI' dialog to set the log directory
+    and voice preferences.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 524644d8d8ce3f8a5a914ecfc7e2a8d474d89095
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 15 22:04:44 2010 +0800
+
+    altosui: oops, missed a file in the previous commit
+    
+    AltosSerialInUseException.java just defines a new exception, thanks to
+    java for making this live in a separate file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 511903704f7e1b22e88dd3e3cc35fd3c0583820e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 14 03:26:57 2010 -0800
+
+    altosui: With --replay option, exit when replay window is closed
+    
+    Otherwise, the application hangs around forever.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 11c95f687b1f68d35fa1a0af2c4e7982b8bb226a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 13 17:09:51 2010 -0800
+
+    altosui: Replace flight status table with labels, fix resize.
+    
+    There's no reason to use a table for the flight status data,
+    replace that with a selection of widgets instead.
+    
+    Also, set all of the grid bag constraints for the various flight
+    status displays so that resize does something sensible.
+    
+    Adds a scrollbar to the table display so that it can shrink.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dcfa56498d1b65a213b8aba9cbd6c4806532383c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 13 16:07:04 2010 -0800
+
+    altosui: Open serial device at 'new' time. Prohibit duplicate opens.
+    
+    With the per-serial UI, there's never a reason to create a serial
+    device without opening it right away. This eliminates the bug caused
+    by not opening the serial device for telemetry reception.
+    
+    Serial devices can now be opened only once; this eliminates errors
+    when trying to reflash or configure devices while receiving telemetry.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8463ffcaca6bcd31e645aba71c171f548dce96d8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 13 15:19:14 2010 -0800
+
+    altosui: Eliminate unncessary import altosui lines
+    
+    Java appears to automatically import every module from the current
+    package.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 566c6486c8815ae241e0b116a93b1a0ff4783831
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Nov 13 12:19:12 2010 -0700
+
+    continuing to work on the docs
+
+commit e68fe9454352087889c560d95797922493117acb
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 14 00:59:01 2010 +1000
+
+    AltosSiteMap: add targeting circles around landing site
+
+commit 1e7e02987276847274493312202d22222c953149
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 14 00:57:45 2010 +1000
+
+    AltosTelemetryReader: actually open serial port
+
+commit 9c32b93ef5fb43558fb0179ea1b047e35b7ed6e8
+Merge: 991541f a6f30fa
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Nov 14 00:29:11 2010 +1000
+
+    Merge branch 'buttonbox' of git://git.gag.com/fw/altos into buttonbox
+
+commit a6f30fae906bd87dff192c5fd4d10df283f99930
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 12 17:02:22 2010 -0800
+
+    altosui: Add RF calibration to TeleMetrum config dialog
+    
+    I think that's the last user-settable value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1bdc6166f3bc5ce3f8e55acb1484923781412e21
+Merge: f111871 5c6a533
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Nov 12 17:32:43 2010 -0700
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit f1118717780a81f9257d2eed7828b66538deb8a8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Nov 12 17:32:21 2010 -0700
+
+    fold in content from Keith's email on the re-flashing subject
+
+commit 7def9dd0d0a4ce9cf7c65de573100e664f278717
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Nov 12 10:58:54 2010 -0700
+
+    first cut at instructions on how to re-flash TM and TD devices based on email
+    reply to a user who asked
+
+commit 991541f57f065f429c6ec425efd6ac731280b2c1
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 12 23:42:42 2010 +1000
+
+    better error behaviour if no map
+
+commit 1bcfa22de7821984149db10cb79913efed36b41e
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 12 23:29:40 2010 +1000
+
+    pull up maps for arbitrary locations
+
+commit beb6c881ec006241c7d2820c64e5381131d41180
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 12 03:24:26 2010 +1000
+
+    make infotable scrollable, revert its fontsize to 14
+
+commit 0327c1da01a3f6ede01f05c1d775651a57fd0c68
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 12 02:08:58 2010 +1000
+
+    tabs -> spaces
+
+commit 81e7b43ecad666e2e2310c7c94184f888bc86585
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Nov 12 02:07:41 2010 +1000
+
+    add site map tab, at least for QRS launches
+
+commit 5394548fa5c7bdbfcc01e8aa19e93e1cf6345e2a
+Merge: 891e629 75f7698
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 10 22:11:21 2010 -0800
+
+    Merge remote branch 'aj/buttonbox' into buttonbox
+
+commit 75f7698b99a661ed17a91748a99699fa6761772a
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 11 16:06:32 2010 +1000
+
+    add compass bearing during descent
+
+commit cc0a730de093c49be2a921101d27622b6f592e92
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 11 15:57:52 2010 +1000
+
+    add compass bearing to voice output
+
+commit 317ec72a34906faad88c6924e634617b074e71db
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 11 15:52:01 2010 +1000
+
+    use grayled.png for off
+
+commit 8503943e3613f8670b128012b12ff14fb54321d7
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 11 15:45:43 2010 +1000
+
+    reduce font size for FlightInfoTable
+
+commit 3ffaa5d1c00b28be20fd4a26deb7bd41d953e92a
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 11 15:43:05 2010 +1000
+
+    read preferences for --replay
+
+commit 1f3e091efdfb2fe6f06a066cac60f5d267b94856
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 11 15:40:37 2010 +1000
+
+    add --replay command line argument
+
+commit b16b873723ee3e5097e6725c59ce191119439ad7
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Thu Nov 11 15:38:27 2010 +1000
+
+    use value_font for values
+
+commit 891e629f6ba20654b614f3ca7211a0f1c92670cb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 10 16:28:19 2010 -0800
+
+    altos: Use grey leds when unlit - easier to see
+
+commit 5c6a5335a057be0219450b4b9889c647d8d4a012
+Author: Bob Finch <w9ya@qrparci.net>
+Date:   Mon Nov 1 14:36:41 2010 -0600
+
+    Updated PKGBUILD-git.altos
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b0d31910da592e2f67c47c8fc3e15ce8135d5094
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 9 23:34:32 2010 -0800
+
+    altosui: Add ascent, descent and landed tabs
+    
+    This completes the set of tabs for in-flight status information.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 22d00785188a880700cd372528189a7a15278da9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 9 14:40:58 2010 -0800
+
+    altosui: Add tab UI with 'pad' mode.
+    
+    This creates a multi-tab interface for flight monitoring
+    and includes a special tab for 'pad' mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eb77e806ded99532dc7eaa39c1893f075b028af6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 9 10:21:34 2010 -0800
+
+    altosui: Create abstract interface for flight data display
+    
+    This allows the implementation of the flight data display to occur in
+    the flight UI instead of the display thread.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a0a92c605e238277c9881545a7226e53b5dbc295
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 8 22:17:26 2010 -0800
+
+    altosui: Fix more calls to AltosPreferences.channel()
+    
+    Oops. Two more.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 94f4a50d6430cc8280cbdaa9f39d3cb858d0e077
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 8 22:10:46 2010 -0800
+
+    altosui: Fix channel setting at serial open time
+    
+    Was using the previous non-device-specific preferences API.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6b17d276271faa8a420a1c8f6be17faaa0c7043c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 8 22:07:04 2010 -0800
+
+    altosui: Create buttons for main actions
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 16916be51d746b1e1057b3219e5bec8f8814259e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 5 23:44:47 2010 -0700
+
+    altosui: Split out flight monitoring to separate window
+    
+    This creates a per-TD (or replay) window to contain the flight
+    monitoring information, allowing multiple monitors.
+    
+    This also adds per-TD preferences for monitoring channel.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ca931b1c484bd6b64617370e81b16e169fdae1c2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 17:47:44 2010 -0600
+
+    update changelogs for Debian build
+
+commit 811ced628d586134224c1b501b40ce9eb435fc7c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Oct 6 16:42:27 2010 -0700
+
+    altosui: Separate out jfreechart and jcommon directories
+    
+    This allows these packages to be referenced separately
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c2c4d515ef9cc2cae8a8f2803e9498bb0794c4ed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Oct 6 16:25:49 2010 -0700
+
+    altosui: Remove ability to graph data in .csv files
+    
+    There's no reason to support these files when the raw .eeprom or
+    .telem files which generate them should be used instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d5caf6f2f4d9257e26aa4305b26c02d1b263fa24
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 17:14:35 2010 -0600
+
+    remove the csv build dep, as that code will be abandoned
+
+commit 43e23a60780191e3c6f61df44c5dd08cc8604c51
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 16:27:57 2010 -0600
+
+    update changelogs for Debian build
+
+commit 8103432bf7adfe6ee5bf3ee42e1672475e6edd5a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 16:26:33 2010 -0600
+
+    update changelogs for Debian build
+
+commit 635066cd35fb24cacf8400a477ea4b4e3dd2c39c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 16:26:19 2010 -0600
+
+    update changelogs for Debian build
+
+commit 4396aaaf34cda3751dcca0eceb05b4ffeb411cde
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 16:25:22 2010 -0600
+
+    add build-dep on libcsv-java
+
+commit 7475ab5804bf3fb12b1b755e850af90d5a15d3eb
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 16:19:35 2010 -0600
+
+    update changelogs for Debian build
+
+commit d8a2f4ceb1c70a6be976a78e2264c961de2f2277
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Oct 6 16:18:45 2010 -0600
+
+    add build dep on jfreechart lib
+
+commit f0d1468ceae065f0cdae6f6ae3323dec5636f073
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Sep 28 17:56:49 2010 -0700
+
+    altosui: Add KML file export.
+    
+    Command line has switches now, --kml and --csv
+    Export save dialog has combo box to select kml or csv result.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5a119fd92532d53e552efe1f7c61e87181fcace0
+Merge: 28da340 82744c3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 27 22:28:07 2010 -0700
+
+    Merge remote branch 'aj/master'
+
+commit 82744c3497d37650b88dee80be7956c4bd1cffb2
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Tue Sep 28 15:27:07 2010 +1000
+
+    Add callsign/serial/flight to graph title
+
+commit 28da3406426437604125d332e4cda90d459df487
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 27 22:26:56 2010 -0700
+
+    altosui: use Altos constants in graphing code
+    
+    The Altos class nicely defines constants for all of the flight states.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce7f59fbfb5a94a67a4ceced3cc371b4c6b6e5d1
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Tue Sep 28 14:45:01 2010 +1000
+
+    Hax0r graphing to support telem/eeprom files
+
+commit e2b9f47a205348d38756c70e928a2a9183de6884
+Merge: 7ef3ad0 8032031
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Tue Sep 28 12:55:47 2010 +1000
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit 803203197e51e71e9c77b3610047fd5bf9a56c56
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 27 18:52:30 2010 -0700
+
+    altosui: Deal with eeprom dates going backwards across wrap
+    
+    eeprom timestamps can go backwards as the GPS time stamps are
+    recorded when the first GPS character is received, but not placed into
+    the eeprom log until the last GPS packet is complete. If this happens
+    at the same time the tick count is wrapping, then the tick count will
+    wrap backwards across the 0 boundary causing time to jump forwards.
+    
+    Fix this by letting time go backwards across the tick boundary, which
+    requires that we know when the first 'real' tick is read from the
+    eeprom file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fe7eba2e4af36cf29d8dc2378ac6985be04f68c6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 27 18:51:05 2010 -0700
+
+    altosui: .CSV output: add link quality, gps hdop and sat C/N0 numbers
+    
+    This makes the CSV files contain all of the available information.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c89a34d1eb25155405b0036baeadc7bbfeade1c2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 27 17:11:48 2010 -0700
+
+    altosui: Create iterables for log file scanning. Split out display threads
+    
+    Convert from log file reading paradigm to using iterators which is
+    more idiomatic for java. Split more code out of AltosUI.java,
+    including the display update threads for telemetry monitoring and
+    logfile replay.x
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7ef3ad0c9354c0484c25badc69334b59c7f355e2
+Merge: eb74866 e66919a
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Sep 24 10:28:06 2010 +1000
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit e66919aa46193bd8c7a1e86fb32a3367dae121f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 23 16:52:51 2010 -0700
+
+    altosui: Require 4 sats for 'good' GPS data
+    
+    Wait for 10 consecutive GPS reports with at least 4 sats before
+    reporting "GPS ready" state.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 34ca8c00f4be72c314baff4c96f1e2f010948454
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Sep 20 06:30:49 2010 -0700
+
+    altosui: remove unused ReplayThread wrapper classes
+    
+    These aren't used now that the replay opener dialog knows how to build
+    a reader from a filename.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fd010661ced6075f82a961625826665a3d8d1efe
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 14:00:04 2010 -0700
+
+    altosui: Factor some UI elements into separate classes
+    
+    Clean up AltosUI by moving the two main tables to separate class files.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eb74866e919e8c661153847871f5a79e66d37296
+Merge: af404b4 1260589
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Mon Sep 20 22:05:26 2010 +1000
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit 1260589976c1a95848b298497fd251c4ee7d3f93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 19 02:52:29 2010 -0700
+
+    altosui: Write raw sensor data to .csv files
+    
+    For data export, provide the raw sensor samples instead of the
+    filtered values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7a4d7110debb88f4e906fee7c46f2badd561809d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 19 02:50:43 2010 -0700
+
+    altosui: Use recorded ground acceleration when reading eeprom files
+    
+    The flight software records 1000 accelerometer samples and records
+    that in the flight log. Use that value instead of using the very few
+    samples recorded in the eeprom before boost is detected. This
+    generates far more accurate accerometer data in the .csv files.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit af404b428bd742039afc25ff3850f76bc92c7c29
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Sep 15 07:24:04 2010 +1000
+
+    Add JFreeChart to Makefile.am
+
+commit 61590b8729831cb138b2ba6b88802c208d114753
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Wed Sep 15 06:51:05 2010 +1000
+
+    Add graphing.
+
+commit 3d64f5a6511529ca53699190f4d54de1ba62a9bd
+Merge: ec6da08 b9623f8
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sat Sep 11 15:15:14 2010 +1000
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit b9623f8ef26491e9fa14e2478295fe6f5cbbd87f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 10 21:07:14 2010 -0700
+
+    altosui: Remember directory containing firmware files
+    
+    Instead of forcing the user to navigate to the firmware directory each
+    time, this remembers the previous directory and starts there.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f2204e0832b14b1edca4266a2cbc272141ecc2b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 10 20:55:05 2010 -0700
+
+    altosui: set default .csv file name in file save dialog
+    
+    This uses setSelectedFile to specify which output filename to make the
+    default in the save dialog.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7e053ae1f2d09347123ac9fa79e46645378b4c70
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Sep 10 10:42:35 2010 -0600
+
+    make the column headers comma separated, too, so they align with the data
+
+commit ec6da0824474e46de842845d7b53fe1a1dde33ed
+Merge: 7c2e411 1031067
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Sep 10 16:11:34 2010 +1000
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit 10310672a83a66dc630718b151d653fc066f8e59
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Sep 10 00:09:25 2010 -0600
+
+    update changelogs for Debian build
+
+commit 9d0e89e8ad8926dc8371fa809835a580ae49711d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 23:04:59 2010 -0700
+
+    altosui: Stop parsing eeprom file after hitting 'landed' state
+    
+    Sometimes there are additional records found in the eeprom file; the
+    reader is mostly worried about not losing anything, so it reads as
+    much as it can. However, the last record written for any flight is the
+    'landed' record, so we can stop looking at the file after hitting that.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 67b6952f7126704478ede5575e5e938d18fcc329
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 23:04:14 2010 -0700
+
+    altosui: Fill in time value of last Eeprom record read from file
+    
+    The last record is handled separately, and was missing the code to
+    compute the time. Sigh.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8b0b584cd0ca7542e65aac0c7897ad7ab4115122
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 22:55:47 2010 -0700
+
+    altosui: Remove debug printfs from AltosTelemetryReader
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a80bfae15f1499c49f7ef47978bf0337d8120892
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 23:57:34 2010 -0600
+
+    rewind packaging changelog, again
+
+commit 78ce3120e5a53858ca0d43c734aa5d28b4948ce3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 23:52:20 2010 -0600
+
+    update changelogs for Debian build
+
+commit 71c85613a28c24c3aad7b4aa3299d8677ef1268e
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 23:51:23 2010 -0600
+
+    rewind packaging changelog
+
+commit 7c2e4114a3a43f919a7a6c967d3f16e5d630f90f
+Merge: ddc83b4 af200f5
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Fri Sep 10 15:50:01 2010 +1000
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit af200f5b84555de0556b52146379f3934774a3f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 22:30:48 2010 -0700
+
+    altosui: Fix telemetry file reader to handle tick count wrapping
+    
+    The telemetry reader was ignoring tick count wrapping, so you'd see
+    time go backwards in jumps. Not useful.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 96ca7051f60ea299e3e05bafbe5717fc83c3afd2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 22:50:03 2010 -0600
+
+    update changelogs for Debian build
+
+commit 2d5e48c5dc0e822fdf430f43804c1e5e79fdbf84
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 21:28:10 2010 -0700
+
+    Add --with-fat-dir configure option to publish finished stand-alone bits
+    
+    --with-fat-dir specifies a directory to copy the finished
+    linux/macosx/windows stand-alone ("fat") packages to. A sub-directory
+    will be created under the specified directory based on the product
+    version number and the files copied there.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dd2b77b2a516a055f29191dcdfeb727e637aae86
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 20:59:29 2010 -0700
+
+    altosui: libaltos.so is built with libtool -- it's in ../libaltos/.libs
+    
+    It used to be in ../libaltos
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 72a5c1258db92d0ddd660bfa875e8e55cab47af7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 20:39:41 2010 -0700
+
+    altosui: Remove some debug printfs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 031fd9a954f2e3447d0150eb4ecc81af7b620dca
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 20:36:12 2010 -0700
+
+    Add firmware and libaltos to 'fat' target at top-level
+    
+    This ensures that all of the necessary bits are available to construct
+    the distribution images.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fcaa480b1dfeb2fd4044f9c4e1b6d50c46fc9984
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 21:29:20 2010 -0600
+
+    update changelogs for Debian build
+
+commit 48f57997452e17564e28fe3e37403f6f63d32dea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 20:24:42 2010 -0700
+
+    altosui: conflating USB product and vendor IDs is a bad idea
+    
+    We've now got a USB vendor ID called 'altusmetrum' for generic
+    altusmetrum devices (old USB ID 0x000A) while the general vendor name
+    for all devices is 'altusmetrum' as well.
+    
+    This patch splits vendors and products into separate name spaces,
+    products are prefixed with product_ and vendor with (oddly) vendor_.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 632d276118ab04de67561104be650f8fb69a0450
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 20:34:43 2010 -0600
+
+    update changelogs for Debian build
+
+commit 1ac3d7e3ba52d1b0dc834eaa5d7886c730eaf307
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 20:34:22 2010 -0600
+
+    initial cut at an altosui man page
+
+commit 5c4e437975054d33604402591e1ea2f314932593
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 20:06:09 2010 -0600
+
+    update changelogs for Debian build
+
+commit 0ea75761416bff299233991e961ba25b6c7dcf89
+Merge: 35d70c9 8ee3464
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 20:05:27 2010 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 8ee3464d8064ebe1694c7b20177878c0d9961451
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 17:54:41 2010 -0700
+
+    altosui: Display error dialog when AltOS JNI library can't be loaded
+    
+    Having an error dialog appear at application startup seems better than
+    simply failing to present the device dialog later on.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4224a7526e51eb8fbf1f0a31bae7ee68c6385095
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 17:52:46 2010 -0700
+
+    altosui: Store libaltos.so in $(libdir)/altos
+    
+    It's not a public library, so hide it away in a subdirectory.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1e4c18777d982061f5e507a4d4f4f1cd4d685268
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 17:49:37 2010 -0700
+
+    altosui: Need to have JVM include path substituted into libaltos Makefile
+    
+    The libaltos Makefile needs to find the relevant jni.h file, which is
+    found in the JVM_INCLUDE path, all nicely located by the configure.ac script.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 35d70c92307dd478bc619b2f6c5a88e155cdad28
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 17:27:07 2010 -0600
+
+    handle versioning of ihx files (poorly) by just wildcarding the file name,
+    which assumes there's only one matching .. probably ok on Debian?
+
+commit c286ada6457579d64e9d8ca44b927258b4a561da
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 16:27:40 2010 -0600
+
+    update changelogs for Debian build
+
+commit 6c0ae7e966a81ece35bccbe89d626a58afe899a2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 15:25:18 2010 -0700
+
+    altosui: remove FATJAR from all-local to avoid building fat .jar file
+    
+    the fat .jar file is used in non-native builds to run from a directory
+    containing all of the freetts jar files along with the altosui jar
+    file. We don't want this on a real install where freetts is installed separately.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 737a64c57a8f979f51c3fa6b3f214520c736cf8a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 15:22:14 2010 -0700
+
+    altosui: hack to make JAVAROOT directory get created before javac runs
+    
+    This ensures that the JAVAROOT directory gets created by adding it to
+    the variable used to set the CLASSPATH environment value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b456bd9d7bcd9d968e43c38eeb6fa6ad8c58f895
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 15:11:41 2010 -0700
+
+    altosui: fix telemetrum.inf FFFE:000A product names on AMD64 and ia64
+    
+    This appears to matter to Vista. Dunno why.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 16980b848651a6b20a0b458446f0a19fb517539d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 9 15:10:25 2010 -0700
+
+    altosui: Add explicit requirement to create classes directory
+    
+    The implicit ordering doesn't appear reliable.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 15da683c2f747baef49c8b8d321a86faede0de30
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 15:51:50 2010 -0600
+
+    update changelogs for Debian build
+
+commit 012e7176954a6b74b3b667317be0f6dc41d7ab70
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 15:51:02 2010 -0600
+
+    add libtool to build deps
+
+commit 3e5824b9a71971ce99bfe38a1e860b421e265f02
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 9 15:43:47 2010 -0600
+
+    update changelogs for Debian build
+
+commit ddc83b4c401be965a9947782becf20cc8c54e6a2
+Merge: afea6c2 3d49d5f
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Sun Sep 5 20:49:34 2010 +1000
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit 3d49d5f69b41c27003dbc5ccf1899014bd13bd99
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 02:05:06 2010 -0700
+
+    altosui: ensure that 'altosui' script is installed. Pass arguments along.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b61fec225ada6a9e252e4c7920101ee18c77cbdc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:55:56 2010 -0700
+
+    altosui: eeprom files place 'boost' time in the flight number record.
+    
+    Instead of looking for the first state change record, use the Flight
+    record to get the boost tick.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6c653a4cba5fef8d49261cf1c024f3e86e9058c6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:52:36 2010 -0700
+
+    altosui: Record flight number when scanning file, not when running
+    
+    The very first record in the eeprom is the flight number, but it is
+    time-stamped with the 'boost' time, and so it gets sorted until much
+    later, delaying the return of data until the rocket enters boost
+    mode. This drops all of the nice pad GPS and state date on the floor.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 38e1d87c8d449866faac026577fefa9a118428cb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:23:53 2010 -0700
+
+    altosui: Use local .class files in the classpath
+    
+    This ensures that compiling only a few changed files will
+    locate the old .class files instead of using a stale .jar file.
+
+commit 9941b05a1d03dafd6cd899b5fe999ed769efb1d6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:21:14 2010 -0700
+
+    altosui: Prevent voice altitude data from queueing up
+    
+    When flight status changes rapidly, the queue of voice data can get
+    quite long. This change does two things -- first, it remembers when
+    the altitude reporting happens due to flight events and delays the
+    periodic reporting until a suitable time after that, second it ensures
+    that the voice data has all been delivered before generating a new
+    altitude report.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4dec5c36702d76dc95beada7c1d3222a638a2cbb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:19:11 2010 -0700
+
+    altosui: Add AltosVoice.drain() to wait for queued speech to finish
+    
+    drain() blocks until all pending phrases have been processed, allowing
+    the UI code to avoid pending data that will end up stale by the time
+    it is emitted.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 410ba89eef9c9817eef81b702966cb88820ff7c4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:13:01 2010 -0700
+
+    altosui: Start idle thread after the rocket leaves the pad
+    
+    This makes the first altitude report time consistently 10 seconds
+    after launch, instead of some random time depending on when the rocket
+    launched relative to the time the device connection was made.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6205547ec7191aab0259a8449520e966a96129e6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:08:50 2010 -0700
+
+    altosui: When replay thread is interrupted, don't make final report
+    
+    Normally, the replay process makes one final report after the file has
+    been parsed. However, if the reading process is interrupted to display
+    something else, this report is just annoying, so don't make it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3d99584fcfe43b22e8581874e0ac77ce3d635d48
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:03:18 2010 -0700
+
+    altosui: Add elevation and range data to main display
+    
+    Reported by voice, it's useful to see these on the display as well.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2d58f319a7c1a6a8ccc6a539722009996ba886ab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 01:01:10 2010 -0700
+
+    altosui: Eeprom files contain only one date; save it.
+    
+    While reading eeprom files, the GPS record is reconstructed each time
+    the system sees the first GPS log item (the time field), but as the
+    date isn't repeated, we need to copy it from the old GPS data record.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b2aa689bf3d61e4a3ebe7c828162d1be20aad0f6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 00:59:54 2010 -0700
+
+    altosui: Remove debugging printf from AltosEepromReader
+    
+    These were in place while validing the GPS data reconstruction code
+    that handles eeprom files missing the first GPS date line due to the
+    record overwriting bug in old firmware versions.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eb0e7a59f0806734a4c959a3ce7c57f71cbe3986
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 5 00:57:38 2010 -0700
+
+    altosui: Return AO_LOG_INVALID instead of exception for eeprom files
+    
+    When an eeprom file contains an invalid line, just return
+    AO_LOG_INVALID instead of throwing an exception. This allows us to
+    replay and parse files with extraneous serial communication.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c3a17c71a45207dd715d537704f161de9219f0d7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 18:49:22 2010 -0700
+
+    altosui: Fix linux fat distribution
+    
+    Again, use 'cp -p' instead of 'cp -a' to get files into the archive
+    instead of links. Also, make the shell script 'altosui' instead of
+    'altosui-fat'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 828e9e4c68e3ac90b6ba2e9fd5f131a9975f7e4a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 18:39:58 2010 -0700
+
+    altosui: Fix up Mac OSX .zip file
+    
+    Must contain 'altosui.jar' instead of altosui-fat.jar.
+    Also, was using 'cp -a' instead of 'cp -p' which made files
+    represented by symlinks not end up in the archive.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 044fd27449c70474f51b99dec25fd23d3c03a559
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 18:20:45 2010 -0700
+
+    altosui: Fix windows installer to ship correct files
+    
+    Was shipping altosui.jar instead of altosui-fat.jar
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 148b927c3b8bc7b7b5c5356392032faf2b1203d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 18:04:04 2010 -0700
+
+    Add top-level 'fat' target to aid building distribution files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aed59e1c057c13e28fd368dc2592aa4628211097
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 17:59:11 2010 -0700
+
+    Add version numbers to released files. Set version to 0.7.1
+    
+    Instead of using git revision counts for version numbers, use explicit
+    versions numbers configured in the configure.ac file. Expose published
+    files with version numbers.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2808bba3efa1cff133cc060dabff06fab8b75388
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 17:58:52 2010 -0700
+
+    Ignore libtool files.
+
+commit bc183400ab93d5902c52851319999cc77f27bc81
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 17:35:17 2010 -0700
+
+    altos: add some SDCDB config files
+
+commit 887b11f6b9c81b9f15348d54017e700ca7dc5e55
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 17:27:17 2010 -0700
+
+    Use autotools for altosui and libaltos
+    
+    This switches from hand-written Makefiles to automake with libtool
+    for these parts of the system.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e844e8a0695e27af6f8e3e37a5e3bcc865b862e3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 01:13:42 2010 -0700
+
+    altosui: Add icons to application and Windows menus.
+    
+    Use the altus-metrum icon for an application icon and a windows start
+    menu/desktop icon.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e9ea28504c646fc25791aab09b9e5faf73e0ac0c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 01:08:34 2010 -0700
+
+    icon: add some icons for application programs
+    
+    altus-metrum.ico: Windows ICO file
+    altus-metrum-16x16.jpg: 16x16 jpg image for java
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f550677df016070430ed38bfa2b2be33f1b8c40a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 00:16:41 2010 -0700
+
+    altosui: oops. renamed the nsis file to altos-windows.nsi
+    
+    And forgot to change the dependency in the Makefile
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c0988ddbf104ea729090c2e7e2a28cc6dc9e90f6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 00:14:15 2010 -0700
+
+    altosui: ignore built files
+
+commit ecb4a09535b6a8da0765010489a96e605dbdeb46
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 4 00:13:11 2010 -0700
+
+    altosui: Add windows installer build using 'nsis'
+    
+    nsis happens to be packaged in debian, and it appears to build usable
+    installers, which is all very cool.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf22923cd8b4f6f954718358b411b5b10d8e7b4d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Sep 4 00:46:29 2010 -0400
+
+    update changelogs for Debian build
+
+commit 59798c6fd11502a9c8b66090c23ba50eb250692e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 3 12:43:45 2010 -0700
+
+    altosui: Catch I/O errors on telemetry device, report to user
+    
+    This catches the USB device being unplugged and makes sure the
+    user sees an error dialog in this case.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 16d8d6a8853d09f683b13f9cda3c3174a0aab130
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 3 12:31:05 2010 -0700
+
+    altosui: Must flush serial line after configuring for telemetry
+    
+    Without flushing the configuration commands to the serial device, it
+    never sees them as the telemetry input thread doesn't flush.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d4f64e95e31e2335470efc15df2ab357b7d197f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 3 11:48:55 2010 -0700
+
+    Revert "altosui: Deal with altos bug setting radio channel while monitoring"
+    
+    This reverts commit ba65e4aeb952a1cf49a77f1e24e235508fcea71f.
+    
+    Testing the old code
+
+commit 71191ecef3ba0e00d0f8a7cd1a24982bfa44ec72
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 3 01:30:33 2010 -0700
+
+    altosui: Allow 'connect to device' when already connected
+    
+    Opening another serial device involves shutting down the display
+    thread (to reset its state) and spawning another one. Shutting down
+    the display thread normally closes the serial device as a part of the
+    process, and if this isn't done before the new serial device is
+    opened, then the new serial device ends up getting closed too.
+    
+    Interrupting the display thread and waiting for it to stop before
+    opening the new serial device solves the problem.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ba65e4aeb952a1cf49a77f1e24e235508fcea71f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 3 01:21:57 2010 -0700
+
+    altosui: Deal with altos bug setting radio channel while monitoring
+    
+    If the monitoring thread is active, then setting the radio channel can
+    sometimes cause the monitoring thread to get stuck. I'm not entirely
+    sure why though. For now, work around the issue by making sure
+    monitoring is off, and the monitoring thread has stopped, before
+    changing the radio channel.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e5ef42c2b22c6639d90631dbbb588f9fd2494385
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 3 01:12:24 2010 -0700
+
+    altosui: Report telemetry CRC errors in UI
+    
+    Telemetry CRC errors can signal problems with TeleMetrum or TeleDongle
+    units, so report them in the UI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3b3aa448f3a0f44137f7530b04b58967ba5f22f5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 2 21:11:29 2010 -0700
+
+    altosui: build Mac OS .zip file to include paths
+    
+    Without the paths, the OS X zip file doesn't create a usable
+    application structure.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cff0d1ef6b338b3d5ad9450d4d5f95df934cb5e4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 1 22:56:34 2010 -0700
+
+    altosui: Post error dialog on invalid ROM config values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8d8980f56a4f2c7d6f2ce667130706e0f04f8ded
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 1 22:56:12 2010 -0700
+
+    altosui: Remove some debug printfs from AltosRomconfig class
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5ee6cd41ed189c3166f76558ecada80917f40652
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 1 22:47:15 2010 -0700
+
+    altosui: Hide internal rom config UI helper function
+    
+    This was getting mis-used by the flash UI causing the rom dialog
+    'cancel' button to work just like 'ok'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9a690c9795e8257d2a3225f905117681668a472f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 1 22:46:04 2010 -0700
+
+    altosui: allow flashing to be canceled from the rom config dialog
+    
+    Was using the rom config class wrong, causing cancel actions to work
+    just like 'ok' actions. Oops.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2f07ad14a16dbf1b75c71784ceae303825c90ade
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 1 22:43:22 2010 -0700
+
+    altosui: Abort flashing if debug port isn't working
+    
+    Check each command going over the debug port and make sure it works as
+    expected. This commit adds checks for initializing the clock,
+    selecting the desired program counter and running the flash
+    program.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cf30343aadd5039627a85319872685f743e64b16
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:55:41 2010 -0400
+
+    update changelogs for Debian build
+
+commit 59a40f6d5a2159b9009a3fa0737bb679efd5b32c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:55:01 2010 -0400
+
+    another distclean fix
+
+commit 59ff9180f11063c257746b895a167179b3a4ff7c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:53:16 2010 -0400
+
+    and a few more distclean fixes
+
+commit 3aafd70257b70b7c11ba9c55749157979bc61ea2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:52:04 2010 -0400
+
+    more makefile distclean target work
+
+commit d5a6ad87c7a9ac03b2e694bed0a54b6cc4322a6f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:50:16 2010 -0400
+
+    update changelogs for Debian build
+
+commit 14fa24ed93b3b1cec08a170004c6fb7f4d74f7e5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:48:31 2010 -0400
+
+    update changelogs for Debian build
+
+commit 83552dfa0d38db9cdf3efc89e64e6c7896467856
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:47:54 2010 -0400
+
+    add distclean targets to libaltos and altosui to all Debian package to build
+
+commit 6f24d2a476759104a10b26b54faff2b18b0e208b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:46:21 2010 -0400
+
+    update changelogs for Debian build
+
+commit d079bfe86ed40ff450ece445cf5f5e3970e44cec
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Sep 2 00:44:30 2010 -0400
+
+    update changelogs for Debian build
+
+commit a470315e5d822a69ef5304512cf73c604c88e481
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 1 20:14:51 2010 -0700
+
+    altosui: Remove Manifest.txt from git repo as it's built now
+    
+    This file is built with appropriate contents for each different .jar file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1177e0a684328422be5adc68093d0091a218a824
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 1 19:53:24 2010 -0700
+
+    altos: Bounds check Skytraq GPS tracking data array
+    
+    Missing GPS serial data could cause the tracking array reset to
+    get skipped, causing the array to be overrun, smashing critical data
+    beyond the array.
+    
+    This was detected using the 'altosui' flash command to program a
+    device from TM. Hitting the USB that hard caused TM to crash with a
+    mutex error (3 beeps) after the ao_gps_task structure was overwritten
+    with zeros.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 775acb89660cdee2f3c54c38297baefe39f2414c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 22:24:09 2010 -0700
+
+    altosui: missed AltosReader.class in the Makefile
+    
+    This caused clean builds to fail to make this file
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bd2b9d958c2b7f846031b076ed51c4fbaaf2d68f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 31 00:20:06 2010 -0400
+
+    update changelogs for Debian build
+
+commit d006c5e1255433181aca4c8e6a277b2d1bc0841b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Aug 31 00:19:37 2010 -0400
+
+    add runtime dependencies for altos binary package
+
+commit c1c7d731e3774883fa0bb5538be225a59334d124
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 19:52:51 2010 -0600
+
+    update changelogs for Debian build
+
+commit c35632efb1919764e4b8581ed6fcf2bedd4bd517
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 19:37:50 2010 -0600
+
+    update changelogs for Debian build
+
+commit 2a004d17a13b4ff52d892bfdecff8ad3d0823f7c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 19:37:17 2010 -0600
+
+    don't build all the "fat" jar deliverables by default
+
+commit 507e429db6638f82c32449e9c5ca06b46da30134
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 19:09:00 2010 -0600
+
+    update changelogs for Debian build
+
+commit 25764fcd1b65c3a5a817afdb5901ac30e8a5f0c0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 19:08:29 2010 -0600
+
+    update changelogs for Debian build
+
+commit 4790f78aead8a816e5b247c022b2998ce3a94053
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 18:48:50 2010 -0600
+
+    add a .gitattributes file, configuring the Mac and Windows binary library
+    files with the export-ignore attribute, in hopes that this will prevent
+    them showing up in source packages
+
+commit 81318e5b7179b0311ab099043ecb04a25d763750
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 18:15:40 2010 -0600
+
+    make invocation of 'install' pathless to work on more Unix variants
+
+commit cbc72399a0f4d7429df0189bcdae683dd491cb9e
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 30 17:56:56 2010 -0600
+
+    continue even if rm's don't have anything to do
+
+commit a9a8d23c877e6f6c76857b7c85e3d43b4da1db27
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 05:49:11 2010 -0700
+
+    altosui: Devices with USB id 0x000a always get listed
+    
+    List 'unknown' AltusMetrum devices anytime the UI needs a device
+    name.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a94900b8862b99b4e317ea0ee3edd2a560f270c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 05:48:23 2010 -0700
+
+    altosui: build debian-style altosui too
+    
+    This adds the dependencies to make sure altosui and altosui.jar get built.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 38ac388baf8125c0644b868a7aaf8eba1bdf990d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 05:28:37 2010 -0700
+
+    altosui: Build linux, mac and windows archives on Linux
+    
+    This adds 'fat' archives for each target OS.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 35d9a8214252dbe79aeb69ae47d2e5c58a654702
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 05:27:45 2010 -0700
+
+    libaltos: Use overlapped I/O on windows
+    
+    Otherwise, reads block writes and vice-versa. Crazy stuff.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c7ba92317ac55272acbde12416448ebd17b983a6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 04:52:00 2010 -0700
+
+    altos: Windows sends USB Out packets of 0 length. Ack them.
+    
+    This was an untested case as no other operating system sents 0-length
+    out packets (they're not necessary). The correct response is to ACK
+    them by clearing the OUTPKT_RDY bit so that another packet can be sent.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 20a472cfe3369200150ea4ff067ceb28968dbcac
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 02:58:23 2010 -0700
+
+    libaltos: Add pre-built Windows .dll
+    
+    This lets us create the windows distribution on Linux.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0300fe581c949232bc52b05fe9c1f6032cad6b60
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 02:56:25 2010 -0700
+
+    libaltos: Add pre-built Mac OS X libaltos.dylib
+    
+    This allows the mac bits to be built on Linux.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5d48c494325524bbeed10e0dc7300ed44e7e208e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 02:53:26 2010 -0700
+
+    Update telemetrum.inf to include all current USB ids.
+    
+    Windows 7 has 'encouraged' us to split out each product into a
+    separate USB ID. telemetrum.inf now has all of them listed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit df34bbe7d1c43b12ab6d610fe810b6e1683e4c21
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 02:49:49 2010 -0700
+
+    libaltos: Improve Makefile
+    
+    Builds Windows .dll correctly now and sample app.
+    Moves linux install target to end so it is not default
+    Adds .NOTPARALLEL to disable parallel gnumake.
+    Removes -g debugging flags to shrink file size.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dd5374b8e660012ae4f8b058454fd101e0749ca7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 30 02:00:30 2010 -0700
+
+    libaltos: Fix windows build.
+    
+    Need stdlib.h to get calloc/free defined, remove debug printfs, fix
+    serial timeouts.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 63c832394a829f41b8f77d075786530536360349
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 23:22:27 2010 -0700
+
+    altos: shut down packet mode cleanly
+    
+    Instead of constantly bashing the packet master thread, let it shut
+    itself down in an orderly fashion. It will shut down fairly quickly as
+    all of the activities in that thread are bounded. Otherwise, the
+    master packet thread might leave mutexes locked and all sorts of other
+    horrors.
+    
+    Tested on Linux and Mac OS X and shown to be reliable.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 43619c13f749b79c096d1e8fdab3d5cfb5fd85f1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 22:42:23 2010 -0700
+
+    altos: Abort radio harder when terminating packet mode.
+    
+    Make sure the master radio tasks don't get stuck waiting for an
+    incoming packet again by aborting the radio each time we poke the
+    tasks.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c4a8569f61eddf690d00337543462235ecbfbe54
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 22:41:18 2010 -0700
+
+    altos: flush pending output when terminating packet mode
+    
+    Just in case the last command sent hasn't been transmitted, hang
+    around for up to a second waiting for the data to get across the link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1acd3c7ec167b1b18e4ea493e5978c938a91cc89
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 21:45:19 2010 -0700
+
+    libaltos: cjnitest needs altos_flush now
+
+commit 6527357d1f0e94faf9e7dacac10a39875131be7c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 21:43:46 2010 -0700
+
+    libaltos: Missing OS_LDFLAGS on cjnitest build
+
+commit b7fa1ea3338f63b8edcf8aacccb5e519ca0b213f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 21:41:40 2010 -0700
+
+    libaltos: Mac OS X cannot use 'poll(2)' on serial lines.
+    
+    Who ships this stuff, anyway? Instead of blocking, we'll poll every 100ms now,
+    otherwise, we won't be able to abort the read when the device is closed. Yay!
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e60c59123232915e808cee23ef89eb1a38ced34b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 21:40:21 2010 -0700
+
+    altosui: discard invalid lines while reading Eeprom flight data
+    
+    This shouldn't happen, but it's easy enough to get back in sync by just
+    skipping lines with weird contents.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ae02b1590439d5c8dfb472cf1f83a14fdcfbaf11
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 21:36:47 2010 -0700
+
+    altosui: provide separate flush_input/flush_output for serial. deal with monitor automatically
+    
+    (yes, this should be two patches, but the diffs in AltosSerial were merged together).
+    
+    First, this replaces the existing flush/flush_reply mess with two simple functions,
+    one to flush output to the serial device, making sure that all data written will be seen
+    while we wait for input. The other sucks any pending input off of the serial line and
+    discards it.
+    
+    Second, AltosSerial now tracks whether the serial line is being used for telemetry
+    monitoring. If so, it enables monitoring, otherwise it disables it. Eliminates a
+    bunch of manual state tracking elsewhere.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit edcfb1bdf64772d3b83405ccf99385b8fea5d8e4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 29 17:33:59 2010 -0700
+
+    libaltos: AltusMetrum devices use more than one USB ID.
+    
+    List all usb devices, picking those with AltusMetrum IDs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 236685807b63860ad033aa0254ce8f6d8d36d4ef
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 22:26:09 2010 -0600
+
+    update changelogs for Debian build
+
+commit 1cda15fdef2d9d3e54354bd5c43a0bcc7e3240cb
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 22:24:51 2010 -0600
+
+    fix up for an 0.7 release
+
+commit 4c5c7c7f198775c398c1ad2edafb3488384cc297
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 22:13:38 2010 -0600
+
+    update changelogs for Debian build
+
+commit 42055af5c6f17d14a2f1c6a2b5e1ce6d3b45a615
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 13:13:19 2010 -0600
+
+    update changelogs for Debian build
+
+commit 0bd4cc03b3bf23aa32b5ce1921078021d1d8a9c6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 13:12:46 2010 -0600
+
+    fix path to installed shared library
+
+commit 99c1d9b4ef10ec4ebbee058ce0bb38c954a0a3a6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:41:26 2010 -0600
+
+    update changelogs for Debian build
+
+commit cf65c6b8056c4af7c26b52ec6f9fbd3400cef638
+Merge: 5f2f6a8 ae5eff7
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:38:25 2010 -0600
+
+    Merge branch 'bdale'
+    
+    Conflicts:
+       debian/control
+
+commit ae5eff7bc0b63047737223423009707bedcb00f5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:37:36 2010 -0600
+
+    Revert "lose the prebuild hook for now while I'm fumbling"
+    
+    This reverts commit a21b6bb60ac1c07ebd161534a4ea63bfde50dcdf.
+
+commit de2e71c4923a0282df74dbe37d087c34b4ddd279
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:25:20 2010 -0600
+
+    fix man page delivery path
+
+commit a8dbe082960dc9bdd44c6e4b1198423c4e566029
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:18:28 2010 -0600
+
+    install altosui man page
+
+commit 5cc933039e4763b8675611c63b6147b42878a2bb
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:16:19 2010 -0600
+
+    fix permissions on installed jar file, switch from ao-view to altosui in
+    the desktop file
+
+commit 138009e9fad01f79df4c3820fbc206f78688bdce
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:06:01 2010 -0600
+
+    update Debian standards version
+
+commit c280071b7db4e9a7af31dc5740eb8d27f137950e
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 12:04:13 2010 -0600
+
+    fix up the wrapper's path to the jar file
+
+commit 5f2f6a8f9ba56be867888758848bc7f152ccbd47
+Merge: 63bd34c 9d1b27f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 27 11:00:31 2010 -0700
+
+    Merge remote branch 'origin/master' into new-packet-format
+
+commit 63bd34cd1b5a411489e8c3ab377f0fe0eec11f67
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 27 10:58:55 2010 -0700
+
+    altosui: add elevation and range information
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 72a18502e40f55cbba6418dc94315517881cd411
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 11:51:24 2010 -0600
+
+    add an install target for altosui
+
+commit 72c33a72ee105ec692dad62d6d9c1ad40b89bfe8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 11:45:19 2010 -0600
+
+    add install target for libaltos
+
+commit a21b6bb60ac1c07ebd161534a4ea63bfde50dcdf
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 11:26:29 2010 -0600
+
+    lose the prebuild hook for now while I'm fumbling
+
+commit 9ea94411c9730f7a271366d309ab4827beeeb839
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 11:17:54 2010 -0600
+
+    add a dummy install target
+
+commit c443f43f8dee6e0fcbcecf9d09e948fd928b7af4
+Merge: 2950431 2923cf5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Aug 27 03:08:53 2010 -0600
+
+    Merge branch 'new-packet-format' of ssh://git.gag.com/scm/git/fw/altos into new-package-format
+
+commit 2923cf5057f9cef110dd547d8677ea5b60e00796
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 27 00:10:29 2010 -0700
+
+    altos: prepare for sdcc 2.9.1
+    
+    A few minor language changes -- non-standard keywords are now prefixed
+    with __, such as 'at', 'interrupt', 'naked'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 68967157cee620ebedcc8c2ffd6fc7656532087b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:55:44 2010 -0700
+
+    altosui: command line args are converted to csv format
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7e0506dc2014b7178f52b950e8c1cb820b35f9c6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:54:53 2010 -0700
+
+    altosui: Remove debug printf from AltosState.java
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 49364608b59de7421ab00d87d2685bc3b5f58411
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:53:06 2010 -0700
+
+    altosui: When parsing saved telem files, errors shouldn't abort file
+    
+    Make syntax errors in telem files just skip the current line and move
+    on to the next one instead of abandoning the whole file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a16db143fc7ca72dc91e7989420049192114642d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:50:51 2010 -0700
+
+    altosui: Serial line is in UTF-8 encoding. Deal with it.
+    
+    We read bytes from the serial line and need to convert each line into
+    a string. So, save the bytes and at EOL, pass the whole mess to the
+    string constructor with the appropriate encoding info.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0942912163255523d923140c01afbdb5da1c19b5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:49:37 2010 -0700
+
+    altosui: Add support for old (version < 3) telemetry files
+    
+    This lets the code read telemetry files from pre-released versions of
+    the software. Not strictly necessary for production, but useful for
+    analysing old files.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e383595cd281687de903fb6176564bbef270cb83
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:47:38 2010 -0700
+
+    altosui: AltosEepromReader was mis-setting boost tick
+    
+    It was supposed to use record.tick instead of the (unset) state.tick
+    value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 651f6102ac79459fc8d5679d852c963dcb5bb3fc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:44:25 2010 -0700
+
+    altosui: add rssi and distance/dir from pad to CSV files
+    
+    Just adds a couple more fields to the CSV files that might be interesting.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3dc67c1401976d6e9e2e942d5a4707a4810a0404
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:43:00 2010 -0700
+
+    altosui: Add AltosGreatCircle constructors
+    
+    This adds constructurs from AltosGPS pairs and also one from empty
+    args (which defines both distance and bearing as 0).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f0fd423d0bf83bc5c3f9d39e9c09397fbe8caed2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:41:26 2010 -0700
+
+    altosui: Move number parsing code to Altos general class
+    
+    This moves these shared functions to the global shared class.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 68b2b66d7574dfd0bd5e3571b8ffad32ca5d2b73
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 23:37:29 2010 -0700
+
+    altos: mark gps date written only after it gets into eeprom
+    
+    Data logging doesn't start until boost detect occurs. As the GPS date
+    is only logged once, if that happens before logging is written to the
+    flash, then the GPS date will never get saved.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aa6c27df5db6bdae59d00affccb891854a6caa18
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 15:59:09 2010 -0700
+
+    altos: print GPS state flags in GPS 'g' command
+    
+    Having the GPS state information can help with GPS debugging.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 34055129b4008f6a9833887b12dee39ffa408002
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 26 15:57:09 2010 -0700
+
+    altos: always rebuild ao_product.c to track git version
+    
+    The git version is built into ao_product.c and saved in eeprom log
+    files, providing useful diagnostics about the firmware revision used
+    for each flight. However, if ao_product.c isn't recompiled, then the
+    updated version won't be included. Force recompilation of this file
+    each time make is run to ensure that the final output contains an
+    updated version number.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 99400fdc0f19ef538fc362dde5c3ab5b7cdac409
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 24 16:43:38 2010 -0700
+
+    altosui: flush replies from serial link when entering debug mode
+    
+    We use replies in debug mode a lot and depend on them matching the
+    expected parameters. The case which caused trouble was using
+    TeleMetrum to reprogram TeleDongle -- sending the 'm 0' command (to
+    disable telemetry monitoring on TeleDongle) to the TeleMetrum caused
+    it to reply 'Syntax Error' which confused the subsequent flashing
+    operation. Flushing that reply gets things back in sync.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ba086cc77273efe5397f60dcaccd1e3771441481
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 24 04:02:27 2010 -0700
+
+    altosui: write USB serial number string while flashing
+    
+    USB serial number is encoded in UCS2 as a part of the string
+    descriptors. Place those right after the other rom config bits so that
+    altosui can find it. altosui is changed to write the serial number there.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 220f3afdaa432c65f8ad45be7cdbe5c8a3616db3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 24 04:01:47 2010 -0700
+
+    altosui: always display romconfig ui while flashing
+
+commit f62b2aa08ebfd912b3c732397d43ff9f6162ec88
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 24 04:01:14 2010 -0700
+
+    altosui: fetch existing romconfig for flashing
+
+commit d93787284c8e514a929edb9f944c98ae0206a33f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 24 03:59:09 2010 -0700
+
+    altosui: Delay mapping Flash UI until flashing actually starts
+    
+    The flash operation may be abandoned before it even starts; this makes
+    sure the UI doesn't flash up on the screen.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7d44cbd621d2b113ac2b802ef17e3d8a660ce7f2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 24 03:58:00 2010 -0700
+
+    altosui: disable radio monitoring while using serial line for debugging
+
+commit 7bd220dfd9b3fb0e42eb90c3b37eb7b4169eb21b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 24 00:29:11 2010 -0700
+
+    altosui: Add ability to create CSV file from telem or eeprom files
+    
+    This creates a comma separated value file to export data for
+    external programs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 634a550149e7c344a22a637ba484f115592b1018
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 23:15:05 2010 -0700
+
+    altosui: refactor logfile chooser dialog to share more code
+    
+    Move file opening logic into logfile chooser as it can be shared that way.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit afea6c264c5ebf12f1d629bd4bc724da86d11b7a
+Merge: 0e17853 9d1b27f
+Author: Anthony Towns <aj@erisian.com.au>
+Date:   Tue Aug 24 00:02:31 2010 -0600
+
+    Merge branch 'master' of git://git.gag.com/fw/altos
+
+commit a55b132668a819cc26478a609cb79bd9190deb9d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 23:01:36 2010 -0700
+
+    altosui: Separate out log file choosing dialog to share with CSV generator
+    
+    This dialog will be shared with the CSV file generating code, so split
+    it out instead of duplicating it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 295043112ccde35092945c286596f9045ee6fa05
+Merge: 2007288 ef8376c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Aug 23 23:11:22 2010 -0600
+
+    Merge branch 'new-packet-format' of ssh://git.gag.com/scm/git/fw/altos into new-package-format
+
+commit ef8376c4dd8262a34e02b6bb9e19e907ac2f4330
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 22:08:30 2010 -0700
+
+    altosui: make default Manifest look for built-in freetts
+
+commit 56b906f535ac2f86bcab71addbbcd376d74f6a73
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 22:03:36 2010 -0700
+
+    altos: Place rom config variables in fixed location
+    
+    The device serial number and radio calibration values are stored in
+    flash, mostly so that TeleDongle gets them saved.
+    
+    Placing them in well-known locations (starting at 0xa0) makes it
+    possible to find the previous configuration and to re-write it
+    easily, without requiring the .map file.
+    
+    altosui doesn't have the .map file parsing code, so it relies upon
+    this new technique. As a benefit, it reads the old values from the
+    device before reprogramming it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4c0c099716197ef7539be0cf55bbb164f6804958
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 22:02:21 2010 -0700
+
+    altosui: Finish device programming code
+    
+    Altosui can now reprogram Altusmetrum devices.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bd2b44ddd61fadd8bf8ee6bf783ce019b1be7cc0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 22:01:38 2010 -0700
+
+    altosui: Remove debug printf from AltosRomconfig
+
+commit c3f57ffdb6c74de90d982eacd604e658ce9b00a5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 22:01:11 2010 -0700
+
+    altosui: flush serial output before waiting for reply
+
+commit 8857ac5e43eac6db8d5594b8864df497a712242b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 22:00:16 2010 -0700
+
+    altosui: remove debug printf from AltosHexfile
+
+commit b1758be01397fd49c441f40852f3558fe9343a2d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 21:58:50 2010 -0700
+
+    altosui: Add lots more cc1111 debug interface functions
+    
+    These are sufficient to program the flash.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f9e80f39bc39e5882bfe75f959b6501cb3277cd2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 21:55:49 2010 -0700
+
+    libaltos: use pipe to wake up getchar on close. use mutexes
+
+commit 86f7b9314b042f2e512fdf35067817e68532867b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 21:54:47 2010 -0700
+
+    altosui: pad TM config dialog values to avoid clipping descenders
+
+commit b8519b8669ff54741dd738ac343fbd2424451247
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 21:53:37 2010 -0700
+
+    ao-dumplog: Fix --remote and --channel options to actually work
+
+commit ebeb13688a9a5442c838641ede6ba0dc92c9a1a4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 14:32:58 2010 -0700
+
+    altosui: Add debug dongle API, split flash UI out
+    
+    Create an API to talk through the debug port on another AltOS
+    device. Split the flash UI out from the flash implementation so that a
+    command line flash utility can be written.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7f8d7978606abe544b1b9b6065c5480ed813b8ec
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Aug 23 11:53:19 2010 -0700
+
+    altosui: Add .ihx file reading code and stub out flashing UI
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2007288da8a83e3aa925e11cc196f1c65aab2e5c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:00:15 2010 -0400
+
+    working on java packaging details
+
+commit 44b26dd550eef789e70082ccaa46d7d430c67bce
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:15:04 2010 -0400
+
+    add freetts as a build dep
+
+commit 0e17853c08f77debef3e8cf82e9cdb6a5079fc9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 22 23:06:15 2010 -0700
+
+    altosui: Set callsign when fetching eeprom data over the air
+    
+    The updated firmware places the callsign in each packet to comply with
+    regulations, this ensures that TeleDongle has the current callsign
+    configured.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 953bc3438b10b21f3d65d292356c4ab2de23cddd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Aug 22 23:05:20 2010 -0700
+
+    altosui: Add TeleMetrum configuration
+    
+    This presents a dialog with all of the user-settable options in the
+    TeleMetrum set for editing. Combo boxes are used for everything except
+    the callsign.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e1463d8e265dfd42c824d90088cd2a51b4cf8131
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 21 17:57:31 2010 -0700
+
+    altosui: Make teledongle callsign configurable
+    
+    Teledongle uses the callsign in packet mode; this provides a way to
+    set that.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 09252ec22d58e946494e4ca2cf367bf3bbe1cc50
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 21 17:09:41 2010 -0700
+
+    altos: Define USB product ID in per-product Makefile.defs file
+    
+    This allows Win7 to tell which kind of device is connected purely by
+    USB id as it doesn't expose the USB product ID string to user space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 22800dc094797e1e0ad99124198809d0360f7556
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 17 18:22:28 2010 -0700
+
+    altosui: Select devices by USB vendor/product ID.
+    
+    Because Win7 doesn't expose the product name, we're swtiching to using
+    the USB idProduct/idVendor values. This patch adds support for
+    selecting devices by those new IDs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d14c96663a1027164fa30ed89b53f5a9d3fdb82b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 17 18:19:43 2010 -0700
+
+    libaltos: integrate Windows support.
+    
+    This adds Windows support for discovery and I/O.
+    
+    The API to the library is mostly unchanged, except that it now exports
+    product and vendor USB IDs as Win7 doesn't expose the product name
+    anywhere that we've been able to find, so we'll be updating the
+    firmware to use unique idProduct values for each product.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9d1b27fa147fc8b765d5be165ebef7ee0f85bd37
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 11 22:11:50 2010 -0400
+
+    update changelogs for Debian build
+
+commit b6da90b4627dde1fe88240c38c51559d8f781dd0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 11 17:15:39 2010 -0400
+
+    update changelogs for Debian build
+
+commit 4918f73fd0a0f3f5d52907f95a0ec385e901d447
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 11 08:50:27 2010 -0400
+
+    update changelogs for Debian build
+
+commit f63f16a70e4d0535667a6364cafcbff026ee6a8c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Aug 11 08:36:59 2010 -0400
+
+    update changelogs for Debian build
+
+commit 294d9c7db21eaf1e71504dbcca5040371abcce55
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 7 22:30:55 2010 -0400
+
+    ao-dumplog: add --channel option (for use with -R option)
+    
+    Sets the channel when downloading data with the -R option.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f317f1324b69b4241f4bb192e164b33d712d5a43
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 7 00:42:25 2010 -0400
+
+    altosui: Start adding code to write csv files from eeprom/telem files
+    
+    This is a start to code which can write out a csv file full of flight
+    data from either an eeprom or telem input file. It's not hooked up,
+    but the restructuring necessary is finished and the output is started.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4738cb2fc639adb1d9237e6c903479f0690dd81a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 7 00:40:59 2010 -0400
+
+    altos: add callsign to packet mode, increase payload to 64 bytes
+    
+    Untested, but it 'should' work. Need to add callsign setting to packet
+    mode users.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b7699a5907e64bc7547fcc27e73f4a35bbaabfff
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Aug 6 13:09:21 2010 -0400
+
+    altosui: Add comments to Eeprom reader
+
+commit 0e917f3ff822616adb147517ac961422e5fedbfd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 5 22:49:53 2010 -0400
+
+    altosui: Compute flight state from eeprom data
+    
+    This lets eeprom files be used to replay flights.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a0a9b445a4d379730b67720f8d7b682d5206a582
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:16:48 2010 -0400
+
+    update changelogs for Debian build
+
+commit e075b8623533965b1b77b77d38c2df32f5f77fce
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:16:24 2010 -0400
+
+    update changelogs for Debian build
+
+commit 410de62715a0830f03b0a65d6c7730dff51e6ef4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:15:04 2010 -0400
+
+    add freetts as a build dep
+
+commit 0b85160c44d934f3d1352c1c07c296d01ceffc32
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:02:17 2010 -0400
+
+    update changelogs for Debian build
+
+commit d091099753d9cae01c4805812425ebea19ec09cf
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:00:46 2010 -0400
+
+    update changelogs for Debian build
+
+commit aed55ef1ce45b0f6e6fefeebf50be97607b31d65
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 15:00:15 2010 -0400
+
+    working on java packaging details
+
+commit d8bf05f7ad55964c9bce0551e58f4ef6c9f721ad
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 5 13:50:18 2010 -0400
+
+    altosui: Split flight record out of telemetry class
+    
+    This will permit either telemetry or eeprom data to be used to
+    construct the sequence of flight events for reply or data generation.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 85a670b5a904d6750d0f179ae307baeb8fc7cbd2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Aug 5 13:40:17 2010 -0400
+
+    altosui: Explicitly initialize Altos class
+    
+    Because the Altos class is never instantiated, the static initializers
+    are never called, leaving the string to state mapping empty. Hand-code
+    the call to the initialer instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 02f17f2cd26189e2676a9dc0d86bd959ed0bc3f4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Aug 5 00:54:05 2010 -0400
+
+    move to science menu
+
+commit 9e8f7f75442303f9bfa99a0435984f5d36863ae6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 31 10:34:21 2010 -0700
+
+    altosui: Split status and info panels into separate files
+    
+    This moves some code out of AltosUI.java into separate files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9c9b35254c693b3ade42b24d1e29eaf31e6ba2aa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 31 10:24:56 2010 -0700
+
+    altosui: Clear displayed data rows as needed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 88e0137a60d7a13ddb7781befa76650e13ad44ae
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 31 10:07:38 2010 -0700
+
+    altosui: Merge gps date and time classes into gps class
+    
+    No reason to split out the date and time information from the other gps info.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1c3b2fe357d6acf28f48aeddd91693f10381be51
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 31 10:05:15 2010 -0700
+
+    altosui: Capture config and version info in .eeprom files
+    
+    Instead of only writing the serial number to the .eeprom file, write
+    all of the config values and all of the version reply to the .eeprom
+    file. The config values, in particular, contain the accelerometer
+    calibration data which is needed to correctly compute acceleration
+    from the captured accelerometer data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e286eb61ad2a90746c1c31f95d26d5edb48738d3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 31 09:57:49 2010 -0700
+
+    altosui: rename AltosEeprom -> AltosEepromDownload, split out Altos constants
+    
+    Renames the eeprom downloading code and adds a new file to share the
+    flight data constants across the various UI modules.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e3a9e3815db3f290e28b40ae02aa654f515cfc37
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Jul 31 10:55:27 2010 -0600
+
+    update changelogs for Debian build
+
+commit 8fc261c2b77bb8aab201a0649a84b5ffa236ce26
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Jul 31 10:55:07 2010 -0600
+
+    see if my new freetts package works
+
+commit 2f114c7ff6b0deddb790d34139bb11ac37f8c0da
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 13:30:36 2010 -0600
+
+    update changelogs for Debian build
+
+commit 7877496d47ce6d25210c0e1c6500666dbfc0876c
+Merge: c71061a 4cf39b1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 12:07:49 2010 -0700
+
+    Merge remote branch 'origin/master'
+
+commit c71061a37d3d3be2855b61cde33d2371989d7681
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 12:06:06 2010 -0700
+
+    Make altosui test script executable
+
+commit 4cf39b13a574cb656999cf329f5b08e7f910604b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 12:57:31 2010 -0600
+
+    update changelogs for Debian build
+
+commit d1848193d8208388e4ccdfbed4e5663a9ba04dd3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 12:56:59 2010 -0600
+
+    add a jdk to the build deps
+
+commit 9ad4984124b6c05114feac4c4ac078dc248ce16a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 11:56:24 2010 -0700
+
+    altosui: remove option to install to alternate volume
+
+commit 2e797b18f0724caf7aaf96f45997998c7416f34e
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 12:50:57 2010 -0600
+
+    update changelogs for Debian build
+
+commit 89109f9dff3ce855d80da166e3362375282f745d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 12:47:37 2010 -0600
+
+    ugh, fixing failure due to aborted build
+
+commit 6fb8546575f6d99676dbb1dce190b0b7cf24b657
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 11:45:56 2010 -0700
+
+    Moved Mac OS packaging to altosui dir
+
+commit 865d5cdf8931ffc796f608e3e12d7c5a70832825
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 12:45:18 2010 -0600
+
+    add swig as a build dep
+
+commit 91d75fb3919f606a1956bf8c6423a8012d99a56a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 11:45:12 2010 -0700
+
+    altosui: Switch eeprom extension back to .eeprom
+
+commit 33ffd89d5a64c991d28bd7369b61e1faa18f605b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 12:40:52 2010 -0600
+
+    update changelogs for Debian build
+
+commit 62b4cc51a0f54ef363cbff46caef80a0afecdea2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 12:40:25 2010 -0600
+
+    update changelogs for Debian build
+
+commit 6f86db5e0360bef26b21336769b7635e3a11e160
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 11:24:47 2010 -0700
+
+    Add Mac OS X packaging bits
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 950f0a8c54e5835ee5d8b0aea360bd8362c21bc5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 11:09:19 2010 -0700
+
+    altosui: construct Darwin application directory
+    
+    This adds the necessary files and build steps to construct
+    AltosUI.app on a Darwin system.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 669c5f52abb972a82ed6efbee7a8c7d20afb5cd0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 10:48:52 2010 -0700
+
+    libaltos: build with java src encoding UTF8
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit efa0e034a161f4852947cbac06537d6ba4422a0e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 10:48:25 2010 -0700
+
+    altosui: remove debug printf
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 53c279b9e96da8b69837ae84038a78ca5707f2a5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 10:45:02 2010 -0700
+
+    altosui: Close serial, join reader thread, free altos_file
+    
+    Separating out the close and free actions ensures that the reader thread will not
+    access freed memory or dereference a null pointer while shutting down the
+    connection to the serial device. Otherwise, a race condition exists between the
+    serial close and the thread join.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b8bc9994d8bfde6116c8a509e70ddf45fc4decce
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 10:24:59 2010 -0700
+
+    altosui: Remove unnecessary freetts .jar files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e33dbbba94ce42295e9fed9f4ba7e46f9eff1517
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jul 29 10:24:09 2010 -0700
+
+    altosui: check for closed serial device before reading
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 27a2d0c00acf78628428c20ab68e2bfba06340da
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 11:02:38 2010 -0600
+
+    update changelogs for Debian build
+
+commit 537492dc961ee6a1945b1041ee3cc0d3f519a42c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 11:01:21 2010 -0600
+
+    update changelogs for Debian build
+
+commit adf6cbcba23e24a3824f7e242ec37baa2750ab94
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 29 10:59:18 2010 -0600
+
+    update changelogs for Debian build
+
+commit d0fd53bdab2f480cd43b6d7010c2094f4fccda91
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 21:49:23 2010 -0700
+
+    Reset GPS ready status when GPS comes unlocked on the pad
+    
+    If GPS becomes unlocked, then report that in the UI and via
+    voice.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5fd225c3a52445ecdc7c04fac5e3d9a0db177c66
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 21:01:41 2010 -0700
+
+    altosui: report rocket ground bearing at landing only if known
+    
+    if state.from_pad is null, then there isn't any data to report.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d2d772164af95a35ea0f5d2413a5be67de9a210f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 20:18:36 2010 -0700
+
+    altosui: Replace device dialog. Center eeprom monitor.
+    
+    This adds a custom dialog for selecting device, which makes it look
+    much nicer on the screen and allows the user to double-click on an
+    entry to select it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ea32290704a8ca468f01172166b561833b20c954
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 19:37:02 2010 -0700
+
+    altosui: Fix Save flight data monitor layout, add cancel
+    
+    Use GridBagLayout to improve the appearance of the flight data monitor
+    widget, add a cancel button to stop loading data (useful if the
+    connection is wedged).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce90f3fe2aa6e23695ccccb36a8e6e614a08ba31
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 17:08:42 2010 -0700
+
+    altosui: Add progress bar for eeprom downloading status
+    
+    This has a progress bar tracking the state and block count while
+    downloading stuff from telemetrum.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6599e9576c3da9325a1731144c1b8bc4943184c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 15:41:34 2010 -0700
+
+    altosui: Add eeprom data capture function. No UI yet.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8a6040e143ecc7830cc1c0114de85f3b72c067eb
+Merge: 024d077 554a97e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 13:29:51 2010 -0700
+
+    Merge remote branch 'keithp/macos'
+
+commit 024d077302f91bdb17abe70d3211ab0949dab8b9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 13:29:21 2010 -0700
+
+    Remove debug printf
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 826061eaca88c0dd75051a6006ef6703c91af595
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 13:10:11 2010 -0700
+
+    Add voice test command for help in adjusting volume.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 71da54a5ce255395376a44586782ab8b6f3b289f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 13:01:52 2010 -0700
+
+    Make voice and channel menus work.
+    
+    Stores voice and channel data to preferences.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e76b9cc32bbcc5176d9bdd6f8d79778024627382
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 12:24:53 2010 -0700
+
+    altosui: Catch errors opening USB devices. Limit list to relevant devices
+    
+    Avoids a segfault when failing to open a device. Limit listed telemetry
+    devices to just TeleDongle units.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 172a2817dde6718724f2b5fad5a7761801446fa0
+Merge: f2a006f 81bf204
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 11:20:22 2010 -0700
+
+    Merge branch 'macos'
+
+commit f2a006fd98045066bdf429cc142d033e9feb0a8f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 28 09:31:09 2010 -0700
+
+    Make ao_log_data re-entrant as it is used for both sensor and GPS logs
+    
+    Because ao_log_data is called from two different threads, failing to
+    make it re-entrant would cause the 'log' pointer parameter to get
+    overwritten if another thread asked to log data while the eeprom was
+    busy writing out a block.
+    
+    This would cause the second thread to re-writing data from the first
+    thread's address, but without re-checksumming the data as the checksum
+    is computed before the log mutex is taken.
+    
+    The bug can be seen by log blocks with invalid checksums.
+    
+    Here's what happens with the ao_gps_tracking_report and ao_log threads:
+    
+      ao_gps_tracking_report           ao_log
+    
+       Writes a bunch of records
+       *blocks* in the eeprom flush
+                                       sets ao_log_data 'log' to global 'log'
+                                       computes checksum for 'log' block
+                                       *blocks* on ao_log_mutex
+       Wakes up
+       sets ao_log_data 'log' to 'gps_log'
+       writes remaining records
+       'gps_log' is left with svid = 0
+       *blocks* on ao_gps_tracking_data
+                                       writes data, reading from
+                                       the current ao_log_data 'log'
+                                       pointer which points at 'gps_log'
+    
+    Making ao_log_data re-entrant fixes this by ensuring that the 'ao_log'
+    thread has its own copy of the ao_log_data 'log' parameter.
+    
+    I made this function take an __xdata restricted pointer so that it
+    could be passed in the dptr register instead of needing to go on the stack.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 554a97ef455c801dcab825815f44520f96f4c3f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 19:29:38 2010 -0700
+
+    Force java source encoding to UTF-8
+
+commit 81bf2042ca39eb106b789e5a08647c3114669358
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 15:29:28 2010 -0700
+
+    Java voice reporting cleanups.
+    
+    Make sure it says something at the end of a log file replay.
+    Make sure it reports max speed after motor burn out, and max height
+    after apogee.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8f2f38f2a9fb0c106e2c6b60cdc205292ce329ea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 10:18:20 2010 -0700
+
+    Java clean ups -- use varargs where possible, remove AltosSerialReader
+    
+    Add methods that format stuff using String.format for voice and serial
+    link, remove AltosSerialReader class and just embed that in the
+    AltosSerial class directly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 734cd15ccff691f851359518ce6118f29dc9f88d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 10:18:09 2010 -0700
+
+    Remove directories as .class file dependencies; it makes them get rebuilt all the time
+
+commit 809feb75e2155e84aebfcc431867edcfd9054670
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 01:22:56 2010 -0700
+
+    Clean up altosui build a bit
+
+commit 3784578a40dcc61f447435cfdf22e13c409cb9c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 00:16:15 2010 -0700
+
+    Add application icons for Mac OS X
+
+commit 2c273710ea9b76ebee4101893f9fe84be8a02354
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 00:11:58 2010 -0700
+
+    Add Mac OS X packaging files for altosui
+
+commit a58c44cd904e5429b807e5c23913051ed6484edc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 00:09:41 2010 -0700
+
+    libaltos: build fat 10.5-compatible library
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4e3285575e0c7d029e799258587e965779990099
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 00:09:18 2010 -0700
+
+    libaltos: make clean remove all built files
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fb8507975c6e081de2e909eca6faaa8f868b609e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jul 27 00:08:38 2010 -0700
+
+    libaltos needs -I. on all systems
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0a782026f6b19e84ffd44f1ae1b466363474bd30
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 26 18:10:07 2010 -0700
+
+    Darwin doesn't have strndup.
+    
+    This provides a private version of this GNU extension.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b51497597868a40df039dd3ca11b35a6258bbbb3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 26 18:04:10 2010 -0700
+
+    Re-enable Linux support for altosui.
+    
+    This steals code from cc-usbdev for scanning the USB tree and uses the
+    same tty code as on Darwin
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e9153c4f2c71ed965822fcfe5112d2bc38506baf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 26 17:04:04 2010 -0700
+
+    Re-enable freetts
+
+commit 17188f36fe18c23bc2eb877ac9a01b7693f4b863
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 26 17:03:47 2010 -0700
+
+    Present list of altos devices in nice format
+
+commit 005e2d6a7bb3b0546b0c1273296875621632ec6d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 26 15:42:48 2010 -0700
+
+    Switch AltosUI to libaltos for device access
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c726d8f6eb861801d7543552beab6ee2c920c96f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 26 15:41:39 2010 -0700
+
+    Add libaltos which talks to USB connected altos devices
+
+commit 05111d5be4d37bedaaee6415d6ee27347e6a112c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jul 26 12:52:17 2010 -0700
+
+    When the EP0 IN buffer is full, don't panic, just skip sending another
+    
+    If the host doesn't pull the IN packet out of EP0 before sending
+    another SETUP command along, the IN buffer will still be busy when we
+    try to reply to the SETUP command. While I don't quite understand why
+    this would ever happen, there's no need to panic about it, just drop
+    the reply packet on the floor.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d0647950b76bfa9942e4f8cf87353f2b724099f4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jul 21 14:26:36 2010 -0600
+
+    fix text since TM only has one led to blink
+
+commit 8eda9fe94a7fd40cb84f50e17e64956f1584ebdc
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:24:32 2010 -0600
+
+    update changelogs for Debian build
+
+commit cd8aa79de72b8b6b3a26d0c2522e94c621b70f13
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:24:01 2010 -0600
+
+    update to latest Debian standards version
+
+commit 3284c7516e302e6db403d18866924ad926ffb2a7
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:20:08 2010 -0600
+
+    update changelogs for Debian build
+
+commit 8f1933717e3acfbcb09191da6e79a7944f91f9d9
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:19:27 2010 -0600
+
+    reflect documentation file name change
+
+commit 2cf1ef555a7feceeb1c333b273c19dd848e8d03f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:15:47 2010 -0600
+
+    update changelogs for Debian build
+
+commit 74c67fc466118b86b4eb5173f7a6886ae220a985
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:14:27 2010 -0600
+
+    add build dep for sndfile
+
+commit 0794ab1e13313fa49b7caf01aef20b052ad78a88
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:10:01 2010 -0600
+
+    update changelogs for Debian build
+
+commit 15a6791ba8f8313b6b55752c3fa7dc254d56dd5c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:08:56 2010 -0600
+
+    update changelogs for Debian build
+
+commit e747954b6a9e71705f619684df8a118a909b1039
+Merge: bd40a5b 695879d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 22:07:22 2010 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit bd40a5b431847c071f5c486d754eca5627e5e3b9
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Jul 20 02:12:03 2010 -0600
+
+    significant update
+
+commit 695879dbccfc5ec6b79698653c58814158f91686
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 14 19:07:02 2010 -0700
+
+    Switch DBG pins to GPIO when using any debug commands. Reboot to restore.
+    
+    If you want to use TeleMetrum as a debug dongle, you need to flip the
+    three pins used to talk to the remote debug port from SPI mode to GPIO
+    mode.
+    
+    This patch doesn't provide any way to get back to SPI mode, so you'll
+    have to reboot the TeleMetrum to write out config parameters or log
+    flight data after using any debug commands.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9ec6be0e92dee01f7aac006ef6f7779c1da1b36
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 3 17:42:36 2010 -0400
+
+    Telemetry code was mis-computing RSSI
+    
+    The RSSI data from the hardware reports in 1/2 dBm increments, and so
+    must be divided to report plain RSSI numbers.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 62294ea3830d3ea261a8761edc9fa6f98201d321
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jun 24 12:38:23 2010 -0700
+
+    update changelogs for Debian build
+
+commit 4766b13d1241fa585907c6f77707d2d4b3ccff75
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jun 24 12:37:55 2010 -0700
+
+    update changelogs for Debian build
+
+commit 52834517c0706b1f16fb81643a42dc1c1997e00a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 21 15:53:33 2010 -0700
+
+    Abort any in-progress radio operation when changing radio channel
+    
+    In monitor mode, the current receive operation must be aborted so that
+    the radio channel change can take effect without receiving a
+    telemetry packet on the old channel. Aborting any in-progress radio
+    operation will make sure that happens.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5933eaa44fe45027b856f1303dd657b974eb53e7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 21 11:44:32 2010 -0700
+
+    ao-postflight: was walking off state.data array
+
+commit 11d155d558d0b121b66f089adee0a47d71f65a78
+Merge: 544003a 24393ea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 16 21:54:06 2010 -0700
+
+    Merge remote branch 'mjb/master'
+
+commit 544003a8da0248fd6f3c62ded86af74ab7cdadf6
+Merge: 267923e 93c1e29
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 16 21:52:23 2010 -0700
+
+    Merge remote branch 'origin/master'
+
+commit 267923e56e22b3635a21f42ef77a3a36158bc273
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 16 21:49:43 2010 -0700
+
+    Add special code for USB panic's.
+    
+    The USB system may panic if the hardware isn't ready for IN data when
+    the driver thinks it should be. This adds a special panic code to make
+    figuring this out easier.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 50ae347bf9de49ccfc92d26888f36e155fb406a3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 16 21:48:04 2010 -0700
+
+    ao-view: disable radio telemetry monitoring during channel change
+    
+    This makes ao-view disable the radio so that the channel change has an
+    immediate effect rather than waiting for a packet on the old channel.
+    
+    Note that this should also be fixed in the TM code itself so that this
+    change wouldn't be required.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 24393eab0ea085f2d0224b59fdc3c00693e5d3a9
+Author: Mike Beattie <mike@ethernal.org>
+Date:   Thu Jun 17 14:04:01 2010 +1200
+
+    Extension to KML output format, and minor bug fix
+    
+    Extended KML output by breaking flight into coloured segments representing
+    flight state. Add extra statistical information to description bubbles
+    visible in Google Earth when clicking on links in My Places.
+    
+    Fix Bugs:
+    * output kml to file provided as argument.
+    * move kml coordinate output code to take advantage of nsat calculation
+    * remove superfluous %9.2f format specifier from raw_file output.
+    
+    Signed-off-by: Mike Beattie <mike@ethernal.org>
+
+commit 93c1e29b07c331a5ca6e0f647b9d2e9266ed3014
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue May 18 23:12:45 2010 -0600
+
+    updates from Bob
+
+commit bb46c9a1da5788a082e4483e576a5a6d3963507b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue May 18 00:26:28 2010 -0600
+
+    update changelogs for Debian build
+
+commit 16c4cae7ebed7bd3ee0f12b8872bf950574f17e8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue May 18 00:26:00 2010 -0600
+
+    update changelogs for Debian build
+
+commit bc89d962c87b50e5487628f8ca25e4443ac4823d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue May 18 00:24:03 2010 -0600
+
+    merge the altusmetrum-themes package
+
+commit 0c6cf621dfd8339b8bc3915750a3147235f1331b
+Merge: 32e430b 563a9dc
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon May 17 23:59:43 2010 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 32e430b8a5f93b312f6359b4d553bad92ed37b19
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon May 17 22:43:19 2010 -0600
+
+    merge in a derivative of Bob Finch's mere mortals guide as a getting started
+    chapter
+
+commit 563a9dcdfef42718370c49f16cc2271642b3e055
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 17 21:30:57 2010 -0700
+
+    Finish basic flight monitoring UI with voice using FreeTTS
+    
+    This captures telemetry data to log files and presents flight status
+    information in audio form using FreeTTS.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 69092ffd23ac1928d5c84413fd00c2423f313fc2
+Merge: 3c2211a cc002c0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon May 17 20:10:46 2010 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit cc002c0a43a02845ba67d1a61828be382f307b2e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 16 22:31:23 2010 -0700
+
+    Fix telemetrum.inf, tested by Adrian and Keithp
+
+commit 3c2211ad7877d622435bc9e8c9c0b01d62be6ff0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu May 13 15:37:37 2010 -0600
+
+    typo fix from Bob
+
+commit f0433c83d76e90d78fb86a1f4a2f145f3c57fb25
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu May 13 01:27:39 2010 -0600
+
+    updates from Bob
+
+commit 0f05cf06e3f1ec510b11d993d9038211ac66c97b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 19:54:04 2010 -0600
+
+    update changelogs for Debian build
+
+commit 6bd85138c1bfbc1d8b78dce1501870a65a64db36
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 19:53:40 2010 -0600
+
+    lose the quotes since they apparently aren't necessary
+
+commit d639144b9bb840fbbced199738787ec054eddf12
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 17:04:41 2010 -0600
+
+    update changelogs for Debian build
+
+commit ae6854df00579ea1c3486f1bf8f19443d5ac9498
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 17:04:13 2010 -0600
+
+    update desktop file for conformance with current standards
+
+commit 7191d74e680a63728f5eb139069a674fef969e6c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 16:45:00 2010 -0600
+
+    update changelogs for Debian build
+
+commit 68eaaa694bb7bf20642ad11eac8a13c1c8e21b7d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 16:44:14 2010 -0600
+
+    add desktop file
+
+commit 9d3d09578381bb6e6f88c39b0e945371bbc22c5a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 16:34:49 2010 -0600
+
+    add desktop file provided by Bob Finch
+
+commit 3cc1dad3b497524ffef61190dd68908cb19e8c35
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 12 16:28:35 2010 -0600
+
+    add initial package build scripts for Arch Linux from Bob Finch
+
+commit 1b8671bd0a00cec6ae4ccf442cd007b18af82fb0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun May 9 01:41:00 2010 -0600
+
+    lots of updates
+
+commit 53b9a9bc28a9278249ff37f92110c21380e93cda
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu May 6 13:59:23 2010 -0600
+
+    update changelogs for Debian build
+
+commit cde60f16f6ab2cdd5010a0e106ae312d144947cf
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu May 6 13:58:31 2010 -0600
+
+    Revert "Merge remote branch 'remotes/origin/fix-reset'"
+    
+    This reverts commit 314d27a73c903fef2968dabac3d5313573713460, reversing
+    changes made to fa77db2ffd8a749c93767db5a6311131e00473ae.
+    
+    For whatever reason, this is utterly not doing the right things today!
+
+commit 8be04df7e1e0a369ffb1137c6e2ec63585e3c935
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu May 6 12:48:00 2010 -0600
+
+    update changelogs for Debian build
+
+commit 314d27a73c903fef2968dabac3d5313573713460
+Merge: fa77db2 823fc0a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu May 6 12:47:30 2010 -0600
+
+    Merge remote branch 'remotes/origin/fix-reset'
+
+commit 823fc0ac27b8df9b4ba1ca0f229b431baf5b46dd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 5 12:50:39 2010 -0700
+
+    Don't change dbg clock while changing reset_n. Use 20ms everywhere
+    
+    This changes the reset code to be more conservative about changing
+    things at the same time, and also sets all timings to 20ms to make
+    debugging with the scope easier.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fa77db2ffd8a749c93767db5a6311131e00473ae
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 5 02:34:41 2010 -0600
+
+    update changelogs for Debian build
+
+commit 8b6767e24a88482dbd3d4c4c969a0be08917d22b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 5 01:32:46 2010 -0700
+
+    Use ao_delay to sleep for 2 seconds instead of trying ao_sleep
+    
+    ao_sleep doesn't delay for a specified time interval as much as one
+    might want it to.
+
+commit e6bb80975fde20928a830170f0821d59a8c72690
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 5 01:31:57 2010 -0700
+
+    Fix all stdio reading functions to be __critical
+    
+    Oh, right SDCC has '__critical' to mark sections of code that need to
+    run with interrupts disabled; no need to use EA = 0 and EA = 1.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ff03cdf746b83542ebcca00d32e6cc69ccfc122d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 5 01:57:54 2010 -0600
+
+    update changelogs for Debian build
+
+commit 8702f497c4278648303eced1aed5bd76d559521a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 5 01:57:11 2010 -0600
+
+    initial attempt at a telemetrum turn on script .. needs work
+
+commit 01cefa181b04e53c20109ef8f3ffff633744da73
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 5 01:56:51 2010 -0600
+
+    update changelogs for Debian build
+
+commit 45a1c2d2dfb69e5269ef2756fcd0f734b48d41cb
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed May 5 01:54:33 2010 -0600
+
+    update changelogs for Debian build
+
+commit f7ff3278bb670df59d7425a014cfe8e3718fea3f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 5 00:44:42 2010 -0700
+
+    Disable interrupts while reading from stdin
+    
+    With multiple input source support, there is a lag between asking a
+    device if it has data and then waiting for more data to appear. If an
+    interrupt signalling additional input arrives in this interval, we'll
+    go to sleep with input available.
+    
+    This patch uses a big hammer by just disabling interrupts for the
+    whole process.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aa9ff021d683764a43800eaa18ea0c9be5134939
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 4 21:42:54 2010 -0700
+
+    Revert "Add optional 's' command to packet slave to enable/disable slave mode"
+    
+    This reverts commit e7dc7fab787df63a4de72c8450e94092eb04d7db.
+    
+    This patch didn't work, and magically appears to break flashing TM
+    from TD.
+
+commit 8c95f33686f69da717013ec2c25dbcd99c03aa45
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 29 17:48:44 2010 -0600
+
+    more text created during SFO->DEN flight
+
+commit af0613ffc178b9b1f011c315923f92f2581fe53e
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Apr 27 00:18:43 2010 -0600
+
+    update changelogs for Debian build
+
+commit 99094f02bf4849ba1f6b9842ded6c39d894320f7
+Merge: 641e76c 75d8ffd
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Apr 27 00:17:37 2010 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 641e76c5d419dab057298541b3a7546877643198
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Apr 27 00:17:15 2010 -0600
+
+    add some RF usage information from an email reply sent today, and re-indent
+
+commit 75d8ffd4eadf31d50b2f58c021530c17ff1bdc66
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 23 13:53:25 2010 -0700
+
+    Autodetect flite voice registration function
+    
+    Old versions of flite exported the function 'register_cmu_us_kal'
+    while new ones export 'register_cmu_us_kal16'. This patch just checks
+    which one is available and uses that.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 97f4874d19ec05c81a04a3ecd06abffcf7fbfafc
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 22 16:25:35 2010 -0700
+
+    More ALtosUI changes
+
+commit e7dc7fab787df63a4de72c8450e94092eb04d7db
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 22 14:53:44 2010 -0700
+
+    Add optional 's' command to packet slave to enable/disable slave mode
+    
+    This option has been selected for teledongle so that you can use slave
+    mode and hook two teledongles together over the RF link.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f4383394b5d2b275b21e3ce8040d8cb9e48bb375
+Merge: 5f93cf8 c879b17
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Apr 18 08:36:07 2010 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 5f93cf8c73555f43c14b1b0757f264bde69e9b8a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Apr 18 08:35:43 2010 -0600
+
+    capture work done on SFO->DEN flight
+
+commit c879b178d83c9a9a521f42a960b10e19b11cee92
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 10 22:09:57 2010 -0700
+
+    Increase reset switch time to 100ms
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b3a2e1221735d54dc3f2b97b4e75ed6f33ab8227
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Apr 10 15:01:14 2010 -0600
+
+    update changelogs for Debian build
+
+commit 9394393c24c0a96b94319f2d0aa78fb498a121c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 9 17:51:01 2010 -0700
+
+    Only have the slave return a packet if it received one.
+    
+    When the receive is aborted to switch modes, it's important to not
+    immediately re-acquire the radio and try to send a packet as the
+    aborting thread won't know to kick the receiver again.
+    
+    This prevents the 'C' command from locking up as it tries to stop the
+    packet slave before turning on the transmitter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce39372a3aeffff1a08d609e63164a00cf974663
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Apr 9 13:50:49 2010 -0600
+
+    wrong Yaesu model
+
+commit a832c7e9d9e9e420e1281136188bd53b34c56464
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Apr 9 00:10:03 2010 -0600
+
+    update changelogs for Debian build
+
+commit c0ee1ae25e1d18138d8372f47085de48ffada344
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Apr 9 00:09:21 2010 -0600
+
+    file changed by auto tools
+
+commit 25e69ebfec94560e0714cf2cc623dc9697b4ea99
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Apr 9 00:08:32 2010 -0600
+
+    update changelogs for Debian build
+
+commit ea5d4f01d18d93d032f05933041b7b6881289780
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 8 22:45:04 2010 -0700
+
+    libflite may forget to reference libasound
+
+commit 4b02f293e9c32a568fad89558274f21157e7d473
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 20:08:07 2010 -0600
+
+    update changelogs for Debian build
+
+commit 5c3b6e2d1989bcaa19ae3e294f297ec3e5648a53
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 19:56:40 2010 -0600
+
+    update changelogs for Debian build
+
+commit 01e524f11a67390a8ea1f20aa2d611909b4da363
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 19:55:05 2010 -0600
+
+    choose a better set of docbook xsl files
+
+commit f93c9bf3695862db31f2c3b3bc5a7bb24ef3766c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 8 17:28:17 2010 -0700
+
+    When changing RESET line, delay 20ms
+    
+    The GPS data sheet suggests a 1uF cap on the reset line to ensure it
+    is held low long enough for the power supply to come up to voltage. TM
+    v1.0 loads a 0.001uF cap there, but in case that isn't large enough,
+    it could be replaced with the larger one. This change makes sure that
+    even with that larger value, the debugging link will be able to reset
+    the target.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit baaaac499cfbc1286ae55374cfdc796d32983b92
+Merge: a4356b9 dec9971
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 8 13:31:23 2010 -0700
+
+    Merge remote branch 'origin/master'
+
+commit a4356b9bcf679c4d7b88fbbad77a98ecb0f80098
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 8 13:30:16 2010 -0700
+
+    Use 16-bit flite voice (which appears to have changed symbols recently)
+
+commit 447c121fc1ceb878e45718ad1364a5349965a59a
+Merge: 10330d2 53ca3f9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 8 11:46:56 2010 -0700
+
+    Merge remote branch 'origin/master' into altosui
+
+commit dec9971d70f17067ba0051206851b49c7604ac85
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:43:03 2010 -0600
+
+    update changelogs for Debian build
+
+commit 6629ec52def8917ad033847812a1adc4c3e9c947
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:42:47 2010 -0600
+
+    lose the url entirely for now
+
+commit a1539a075a0cc79c9122fea878d9a20ee722a18c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:41:42 2010 -0600
+
+    update changelogs for Debian build
+
+commit 934434ffb3514fe9ff95692784750d7c5217a5d3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:41:28 2010 -0600
+
+    fix typo in url
+
+commit 8a067cd0eebbec313fc39086747ef618f2d1cf37
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:36:18 2010 -0600
+
+    update changelogs for Debian build
+
+commit 05ad58389fa3814ecb56344bf4ec3a3e025920a2
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:34:54 2010 -0600
+
+    need another build dep
+
+commit 6fbdc7037db185f03bd5ff96b9d9320646572df7
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:28:49 2010 -0600
+
+    update changelogs for Debian build
+
+commit 8f1d47e9cd61738e516d15fc97d5730d80611e87
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 8 12:28:20 2010 -0600
+
+    update changelogs for Debian build
+
+commit 10330d23518c94a8b791193a97a6cc07b1c9a97c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 6 00:58:00 2010 -0700
+
+    Enable telemetry monitoring
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9e10e43eff9de3f034da49c4f88728fb933f5035
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 6 00:56:57 2010 -0700
+
+    Tasks may move in task structure as a result of ao_exit
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a7fc7901cd591c93d9d0cffeec2977ebb17554d4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 6 00:55:19 2010 -0700
+
+    TD reports "not-connected" when GPS has 0 sats
+
+commit e064d05da87926c19fb665b40fb280fb59328183
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Apr 6 00:54:52 2010 -0700
+
+    serial port read function cannot be interrupted. poll every 1 second
+
+commit c099a67d9ea37e731e0eca318102560281ac240f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 5 22:42:05 2010 -0700
+
+    Interrupt running replay thread when starting another replay
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cc600a0389720bc7e435dbda8bec080ef19e0c58
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 5 22:21:46 2010 -0700
+
+    Add Linux device discovery
+    
+    AltosDeviceLinux.java scans /proc to locate suitable devices. This
+    will be hooked up to the UI shortly.
+
+commit c28646d72005daeadb70b95fd3b0050bd752cc55
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 4 20:55:30 2010 -0700
+
+    Switch TeleMetrum from v0.2 to v1.0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d22ba55ae0e056530a727df50f14ad853d79a2c8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 4 20:55:18 2010 -0700
+
+    Clean up some altosui comments
+
+commit 6251e89c6eea655769f77bc18e98e79c99cf3cad
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 4 19:54:46 2010 -0700
+
+    Don't abort the radio when enabling telemetry monitoring
+    
+    If telemetry monitoring is already on, then there isn't any point, and
+    if it's not on, then presumably there isn't any radio work to abort.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b0b99f30c4e00689e9faceb363a5c7284541c6be
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 4 19:48:50 2010 -0700
+
+    Make ao_radio_idle keep trying to get the radio to idle.
+    
+    Attempting to abort a radio operation could lead to a hang if the user
+    of the radio jumped in and started using it again before the task
+    attempting to abort woke up. This change just keeps smacking the radio
+    until the radio goes idle long enough to detect it.
+
+commit 0e7abc9fedec568b431c983d3df1b0b29f4f10e3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Apr 4 16:32:04 2010 -0700
+
+    Use RXTX for serial comm. Add logdir preference saving
+
+commit c66eebad323e4572bb7cc23bc476ee144f03e9b8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Apr 3 08:02:44 2010 -0600
+
+    rewrite urls in docbook format
+
+commit 3d34c488c5b71020d86f83156fd821fd860bf214
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Apr 3 00:02:44 2010 -0700
+
+    Make .jar file
+
+commit 4bea4c327e002ce8f88218f0d840af7c1521bc35
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 23:25:02 2010 -0700
+
+    Remove unused cell renderer class
+
+commit 4ad062969ae8a608b8428620579bbe114e580a11
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 23:20:38 2010 -0700
+
+    Remove GPS data missing from skytraq. Save max height/accel/speed
+
+commit ebd49d4ec6b0b60c85b2de45cfe2e36add8fe9bf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 23:05:40 2010 -0700
+
+    Report current gps nsat, not last locked nsat
+
+commit 3f9b66b307ee88172151e3bee58e50f5acbde109
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 23:00:30 2010 -0700
+
+    Clean up GPS data formatting
+
+commit 9cc48698ec14c34d437baad7b6540edc31e9741c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 22:47:40 2010 -0700
+
+    Fix state updates
+
+commit 6d523ee4dad3b9890d3cf05852459101fe7e26ea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 21:48:41 2010 -0700
+
+    Fix status update
+
+commit caa0bf49668344937483190d1c258bfa32971d19
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 21:44:00 2010 -0700
+
+    Fix up table formatting
+
+commit a579402f428dd6a0529505069d1846f70b83ab5d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 18:10:59 2010 -0700
+
+    Display table of flight info. gps is not working yet though
+
+commit 65079f84ea64220fa928c3ad96652fed159bf74b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 16:07:40 2010 -0700
+
+    Steal C code from ao-view
+
+commit 02f2be90879b682b6e648cf2debc83223d127b9d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Apr 2 13:37:52 2010 -0700
+
+    Add telem parsing code
+
+commit 8c600abf87c95f8f214b5e56ff6eab955795dff5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 1 23:56:47 2010 -0600
+
+    crudely incorporate "day in the life" info from web page
+
+commit 53ca3f98aeb70cb780031fee788de950e4388cf6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Apr 1 23:39:42 2010 -0600
+
+    tweak copyright assertion
+
+commit 6454e309858aeef7912e862de8632618d89b4205
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 1 18:31:14 2010 -0700
+
+    Fix windows install file
+
+commit 584ab100640a07dec6e06829e73b7260d17d2232
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Apr 1 18:30:56 2010 -0700
+
+    Add Windows install .inf file
+
+commit a06bee96e648d9ded8776f3d6cba9505e7be1a60
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 31 23:05:03 2010 -0700
+
+    Add telemetry data parsing code
+
+commit 7f233369e32c3254165ee251df0a3dbc21ea5a29
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Mar 31 13:49:54 2010 -0700
+
+    Start adding java-based UI
+
+commit 76768804e68ed09421d7a48cb0b390f102ce2d76
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Mar 30 23:26:35 2010 -0600
+
+    make lintian happy
+
+commit eb3cc3e9b60ec23acbb7d797affa743d671801ab
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Mar 30 23:19:48 2010 -0600
+
+    update changelogs for Debian build
+
+commit 00f49c8fb0aa38331360bdb85c32bcebe60dcdc3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Mar 30 23:19:07 2010 -0600
+
+    fix typo
+
+commit f03ed0876c67b58624abf0c14bf73444b0322d3c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Mar 30 23:18:37 2010 -0600
+
+    update changelogs for Debian build
+
+commit b41e617080fe825f7810ee5eee52ea37f7618ec6
+Merge: 28e40cc df7bda1
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Mar 30 23:15:32 2010 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+    
+    Conflicts:
+       ChangeLog
+
+commit 28e40ccfcd80ab8764d4aa235257cea4d193a0c1
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Mar 30 23:14:47 2010 -0600
+
+    update changelogs for Debian build
+
+commit 20d4d410e0fc04fe192e309811eed6c0194fa5a8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Tue Mar 30 23:11:40 2010 -0600
+
+    initial harness for documentation
+
+commit 9801ff7de21027935f52ccabaa3ff157e22d21ce
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Mar 15 12:04:18 2010 -0600
+
+    move gbp.conf into debian/
+
+commit df7bda1f32b0049c3878c325ea0b55999f3980e3
+Merge: 23da4f3 a7042fe
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 12 10:38:26 2010 -0800
+
+    Merge remote branch 'origin/master'
+
+commit 23da4f3bcdd1d780c9e1f6b68ad2fb309fcae6ba
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 12 10:37:32 2010 -0800
+
+    Document the ao-dumplog '--remote' flag for radio-link downloads
+    
+    The --remote (or -R) flag uses TeleDongle to fetch data over the radio
+    command link from TeleMetrum. It's been there for a while, but the man
+    page failed to mention it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 93df98898f8cd199ae13158bc4f65e3494c954ad
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 12 10:35:12 2010 -0800
+
+    Round radio calibration value instead of truncating
+    
+    The radio calibration function in the bring-up code was truncating the
+    radio calibration value which caused the resulting frequency to
+    always measure above the target frequency, instead of trying to get as
+    close as possible. This change will result in a closer frequency
+    match, but may sometimes be below the target frequency.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a7042fe7d065d1da1252be5ad43e3c9856214dc8
+Author: Bdale Garbee <bdale@Deborah.(none)>
+Date:   Fri Mar 5 19:11:11 2010 -0800
+
+    update changelogs for Debian build
+
+commit eda5e1166a97766aa22561beaa1086ba4e19ee16
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Mar 4 17:33:39 2010 -0700
+
+    update changelogs for Debian build
+
+commit 6e61170d42936c18cd6efba6f4c14af616a30745
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Feb 28 18:24:46 2010 -0800
+
+    Need to duplicate new altitude conversion code in aoview.
+    
+    Altitude conversion is now done with a smaller table and interpolation
+    instead of a giant table.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e4e17e6d3844ae682a0e7a9469a522359bac77b6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Feb 27 17:36:13 2010 -0700
+
+    update changelogs for Debian build
+
+commit a1478f65538fdaac7b58ffbd958a035b74956099
+Merge: 901fce5 bbf8c9f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 27 15:19:33 2010 -0800
+
+    Merge remote branch 'origin/master'
+
+commit 901fce5fe3e2762406079ecaa787612b5ed4b34a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 27 15:18:47 2010 -0800
+
+    Add .gitignore for ao-bringup
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7aab73a265841aac817ea34235dd1eb819debf76
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 27 15:14:04 2010 -0800
+
+    Fix up LED colors for each product.
+    
+    Different products assign different color LEDs to the two available
+    LED drivers (P1_0, P1_1). Make the LED color pin assignments
+    per-product (in ao_pins.h), then deal with not always having a green LED.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1d6d8d76ec65373ffee70add75d183f5c4168f61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 27 15:11:44 2010 -0800
+
+    Eliminate deadlock when writing config from radio link
+    
+    Writing the 'Saved\r\n' string would fill the packet buffer and cause
+    a flush to occur, which would need to wait for the radio link to
+    receive and transmit a packet. The radio link always re-fetches the
+    radio channel number when lighting up the radio, so it need to look in
+    the config space. If the config mutex was held by the config writing
+    process while the radio was trying to get the channel number, then
+    we'd get a deadlock.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f1956ebadd7bf46b84a05a0c383b6404ca26b344
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 27 15:10:16 2010 -0800
+
+    Leave .ihx files in the build directory too - easier to debug that way
+    
+    sdcdb wants the .ihx and .cdb files in the same directory, so humor it
+    by copying the .ihx files to the src directory instead of moving them.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bbf8c9f1748af3f1fac08ddf80ae98da2e9b5727
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Feb 26 15:21:12 2010 -0700
+
+    create a turn on script for lighting up TeleDongle v0.2 boards
+
+commit 461d4a1948e112ec7353caf88967391d876469dd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Feb 26 10:33:13 2010 -0800
+
+    Add LED test
+
+commit 5d7a3a5fbc0af4621c67a6fd51a9c9d5ae688fa5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Feb 25 16:40:13 2010 -0800
+
+    Fix and document the ao-rawload --run flag
+    
+    This allows ram-based programs to be loaded and executed easily.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1e60deca147c85a064719dfad14ccabd1049bbbd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Feb 25 16:33:34 2010 -0800
+
+    Allow product names to have suffixes (like board revisions)
+    
+    When looking for a board by product name, just look at the prefix of
+    the name instead of requiring an exact match. This will allow products
+    to have board version suffixes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f4d5790a284e2d02dd7568fbca90402fa5ed1aea
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Feb 25 16:32:57 2010 -0800
+
+    Add ao_radio_xmit to help test boards without flashing them.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d7d551b0078acb1596a9b9023c3df6dbfa46213c
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 17:30:23 2010 -0700
+
+    update changelogs for Debian build
+
+commit 2f45953ee54034209a23c254e65da36e44cf075f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 17:29:47 2010 -0700
+
+    un-muck changelog
+
+commit 540f86678853e7ea80ae91f3f2ec0ec88c5b1ca7
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 17:27:51 2010 -0700
+
+    debugging
+
+commit cdccd92f6ed97be7385e84de5694064cb8f43946
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 17:25:28 2010 -0700
+
+    conditionalize use of git on executability of /usr/bin/git binary
+
+commit 54dd7a200e6956aace5a580d4c4d6a10a13c654b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 17:22:45 2010 -0700
+
+    update changelogs for Debian build
+
+commit 4b6b28530206c6fdbe46699b81746fbbcab5b148
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 17:22:20 2010 -0700
+
+    see if this works
+
+commit c74958d3e8c5ce8006a9f9f6853238fce0c77432
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 17:13:29 2010 -0700
+
+    move git-using variable declaration into the prebuild target
+
+commit cf091a7252eba09902a42a3cfe7b4d6a343907ce
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 16:44:44 2010 -0700
+
+    update changelogs for Debian build
+
+commit deccc10f0305c0cd02bf6317d6dc9d2ae38c3dac
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 16:43:32 2010 -0700
+
+    add gawk as a build dependency since strtonum is a gawk extension
+
+commit ac45da6e61597dcdb119b976f7301b2cc7fdced4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 14:57:26 2010 -0700
+
+    update changelogs for Debian build
+
+commit 24912821b3230b8357b9e0094cd69fa0bccc5513
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Feb 24 14:53:27 2010 -0700
+
+    update changelogs for Debian build
+
+commit 82fdbfe1229d1ea5e6906ea240d54b3da73d9a69
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 20 21:35:43 2010 -0800
+
+    Must install .map files for ao-load to work
+    
+    ao-load uses the .map files to rewrite the serial number and other
+    config parameters into the program flash.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a5215c2bd9249a285fc834db0c453fb3e7daed87
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 20 21:20:05 2010 -0800
+
+    Add back stack size checking to altos linking phase
+    
+    This verifies that the stack start specified during the compile
+    will work with the resulting program
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 18eeb79026f7e5c54bf99435537c024427011a36
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 20 21:05:45 2010 -0800
+
+    Quiet make output.
+    
+    This borrows ideas from the notmuch project to reduce the command line
+    clutter seen when compiling or linking stuff.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 004c2a9b7a4b44ef71ca60482573e8e9d8c6ce93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 20 20:36:08 2010 -0800
+
+    Update .gitignore files
+
+commit 84c93bb2fc4558a5e4654794ba90e730a84eaf67
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 20 20:22:16 2010 -0800
+
+    Change altos build process to support per-product compile-time changes
+    
+    This creates per-product subdirectories and recompiles everything for
+    each product, allowing per-product compile-time changes for things
+    like peripheral pin assignments and attached serial devices.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fd0a42e0e96dcb8ecc9e999f70bcf70692692af9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 20 20:21:07 2010 -0800
+
+    Change barometer conversion code to shrink conversion table
+
+commit 876e9a10b9096ead85fbe08ec9a6a0329cf7cbd4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 13 16:42:27 2010 -0800
+
+    Log GPS data on pad after boost detect.
+    
+    This wakes up the two GPS reporting tasks and gets them to report out
+    any existing GPS data to the log file. To make sure the timestamps in
+    that GPS data are accurate, this also records GPS time on receipt of
+    the GPS data instead of when that is logged.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c83615567b4567f3dc45a7f7b894943b45fbb65c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Feb 7 00:25:22 2010 -0800
+
+    Pull in a bit more data for filtering the start of the boost
+
+commit f8967607b3dda0c0ce7afe8bb077da2da5ed3dcd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Feb 7 00:24:56 2010 -0800
+
+    Compute daytime using GPS as time base
+
+commit 2a6350149407c7d2e7d143906c40c5e331248aeb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Feb 11 18:48:56 2010 -0800
+
+    Missing ao_mutex_put in gps_dump
+
+commit 9856b7c4397afcecc8f541af9a83824e817b3612
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jan 10 16:31:50 2010 -0800
+
+    Switch to using internal cc1111 temperature sensor
+    
+    v0.2 has no temperature sensor, and several of the v0.1 boards didn't
+    get a temperature sensor loaded. Use the internal temperature sensor
+    on the cc1111 in all cases instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0c2533be15858774ef9381aa8c8344356fd5b971
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 22:06:19 2010 -0800
+
+    Force idle mode by shorting the SPI clock to ground at boot time.
+    
+    This allows you to override the flight mode detection code in case the
+    accelerometer calibration is broken somehow. Hold the SPI clock shoted
+    to ground until the LED comes on, then remove it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 28346736a7799c0767e54511d9949cd61d35e471
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 20:57:48 2010 -0800
+
+    Add simple gps dump command 'g'
+    
+    This just dumps out the data in a very simple format to verify the GPS
+    receiver.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a6082a8b4501ac4cb18584ace1f0c40e088e5484
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 20:57:29 2010 -0800
+
+    Remove flash debugging printfs
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 05bb953830604721c9a49dd56256b86f3666b5ff
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 20:42:57 2010 -0800
+
+    Don't set ao_flash_setup_done until we're actually done.
+    
+    Because we're accessing this variable outside of the mutex, we need to
+    make sure it isn't set until the data it covers has been initialized.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 327e5e782958e516a7b7a786ab6c176b0decb8b8
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 20:04:42 2010 -0800
+
+    Dump config block from read/write config and flash_status commands
+
+commit 05e5bb2d330b755967d06fb859585c81f5f5fbb5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 19:46:46 2010 -0800
+
+    Dump more flash parameters for the flash_status command
+
+commit d4c8895b349998e02e03ed83466a0ca7afb3d99b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 17:57:52 2010 -0800
+
+    Add 'f' command to display flash status register contents
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c1334f712f01543adb10809bc1a3ca120e27290b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 09:55:04 2010 -0800
+
+    Add at45db161d.h header file for new flash part.
+    
+    Forgot to add this when I added the driver.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a0d4c1b06d27e850d233f8ddf8fe32912bf0ec8f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 01:29:41 2010 -0800
+
+    Remove green LED and temp sensor from v0.2 code
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dc8d18736239b14c2ec48a40a01515912c5c25e6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jan 9 01:22:06 2010 -0800
+
+    Add AT45DBxx1D driver
+    
+    This driver supports the AT45DB011D through AT45DB321D DataFlash
+    parts as found in TeleMetrum v0.2
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3bab7e66d25988f5d63ec139c8814a85b983f8f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 19 20:32:18 2009 -0800
+
+    Remove dbg driver code from telemetrum/teledongle
+    
+    The only board with debug outputs is the TI dongle at this point, so
+    the debug modules were disabled in TM and TD some time
+    ago. Unfortunately, the code was not removed then.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eddb82390a0ecb07ef83c04861993842906b03ab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 19 15:31:36 2009 -0800
+
+    Ensure that ao_alarm waits at least the specified time
+    
+    Because the timer tick may happen soon, it's important to delay by
+    another tick to ensure that we don't wake up early.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 598d168bd6552c3756e4b0267de44147eadab9f6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 19 14:03:11 2009 -0800
+
+    Disable monitor mode before attempting radio test.
+    
+    If monitor mode is left active, then the radio lock will not be able
+    to be acquired for the radio test.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d4cf1446680d8b47396bcda338e8df7af395d102
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Dec 19 13:53:38 2009 -0700
+
+    update changelogs for Debian build
+
+commit 10d1bbcd9709a5eee8d50989215242b16feb7232
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 19 11:34:16 2009 -0800
+
+    Use ao_radio_get/ao_radio_put in packet code.
+    
+    The ao_radio_get function both acquires the mutex *and* configures the
+    radio channel and frequency. Failing to use this in the packet code
+    would leave the radio frequency unconfigured.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a15abc1882a3bdd2c980eed169f3b80337528390
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Dec 19 12:15:29 2009 -0700
+
+    update changelogs for Debian build
+
+commit bbb152c712801653374a8f82869e2e8bf41f7279
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Dec 19 12:05:40 2009 -0700
+
+    update changelogs for Debian build
+
+commit 7ac8efea5d60b81adccdc1e38ac4c13facfae7c9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Dec 15 23:58:29 2009 -0800
+
+    Add --cal to man page
+
+commit 5481082b18226a0de6b377215b3b330bdbc4a6c6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 5 19:50:38 2009 -0800
+
+    Allow radio calibration to be set from ao-load
+    
+    This moves the initial radio calibration value into const memory where
+    it will be used if no eeprom configuration value is available, either
+    on an unprogrammed board with eeprom or a device without an eeprom.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 46f03ab3145a61139c8ca6fc99e8f2798728b5a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Dec 5 15:36:12 2009 -0800
+
+    Re-order config values. Change frequency to cal
+    
+    Place more often used values at top, and consistently call the radio
+    value 'calibration' instead of 'frequency'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bf29a62532fec12e6af2d2f3a6624882c863e933
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Dec 5 11:03:42 2009 -0700
+
+    update changelogs for Debian build
+
+commit cd49847f3125df1733f298b56a43e8027ab5ce05
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Dec 4 23:38:26 2009 -0800
+
+    Add radio calibration configuration.
+    
+    The crystal we use is only good for 20ppm, which generates a fairly
+    significant error bounds at our RF frequency. This commit adds a
+    configuration variable that sets the RF frequency control variable so
+    that the output frequency can be adjusted.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c671a3f31f54715284beef5baa4a72ca922e4018
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Dec 4 23:36:54 2009 -0800
+
+    Make ao_cmd_decimal produce both 32 and 16 bit values.
+    
+    ao_cmd_lex_u32 is produced in addition to ao_cmd_lex_i so that
+    functions can easily read 32-bit values from the command line.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e6d6ad66ce177eb6e49eaa04b1ecc6426f4bbdbf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Dec 4 23:35:40 2009 -0800
+
+    Remove send_serial and serial_baud commands.
+    
+    No longer useful, and they take up space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce7325cefbbe91054c1e8174cf40a6c687f6694b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Dec 4 21:02:07 2009 -0700
+
+    update changelogs for Debian build
+
+commit 9a1d7dd78c07d18954ef620b6ed25d6701df21e2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Dec 4 14:35:39 2009 -0800
+
+    Change default callsign to "N0CALL"
+
+commit 291e2004e77debdc1543912cf11043c849305de8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 26 11:18:37 2009 -0700
+
+    update changelogs for Debian build
+
+commit 40533095a96b6c6364eebdc4b7d53c4eabe72e9a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 26 11:16:04 2009 -0700
+
+    update changelogs for Debian build
+
+commit ab1075e7219b02258c1613d93379582be4168947
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 26 11:15:13 2009 -0700
+
+    change home URL in control file to be the AltOS page
+
+commit d34dcc5f3616e59ee90ed172770fe2a3eb1e0cac
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 26 10:40:07 2009 -0700
+
+    fix absolute path in debian/dirs, add Suggests for slim-altusmetrum
+
+commit ea40561b36519a5dcabedabe18672b79ea5e9993
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Nov 22 10:18:44 2009 -0700
+
+    update changelogs for Debian build
+
+commit d6ba07e885bdc62ba64719c9d8cc42fcecbcb09d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 22 01:10:44 2009 -0800
+
+    Automatically extract flight number for eeprom and telem filenames.
+    
+    Extract flight number from either telemetry or eeprom files and use
+    that in the resulting filenames. To ensure that files remain unique,
+    add a new field, -seq-%03d. This is appended only when the sequence
+    number is non-zero as it shouldn't occur in normal usage.
+    
+    This also eliminates some duplicate filename creation code in the
+    library and aoview sources.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 06cebd1026dc1bd6ee51526fa2d02905df3b3b37
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 22 00:52:58 2009 -0800
+
+    ao-postflight: don't try to use missing gps sat data
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9ada1b538af3308e1b22bd024d9204521184173
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 22:12:21 2009 -0800
+
+    ao-postflight: compute barometric alt for each GPS position
+    
+    Print that to the --gps file, and use that in the --kml file for the
+    altitude. Gives a very different picture of our flight tracks,
+    presumably far more accurate (at least in altitude).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b84b634d9ae8ce6ab1c02833a3ed8514404e1ca3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 22:11:45 2009 -0800
+
+    Don't crash if --plot isn't passed on ao-postflight command line
+    
+    Crashing is not nice.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2d77c18b15834046b7b79d49d87211828f2409e9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 21:10:09 2009 -0800
+
+    Convert telemetry file GPS satellite information in cc_log_read
+    
+    The satellite info wasn't being correctly converted from telemetry
+    files to the data log structure, so ao-postflight was not seeing it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1e7fb61700f1f6f2ed5fdbc4380d7187b0cd187b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 21:09:03 2009 -0800
+
+    Fix --plot arg handling. Add -all option.
+    
+    The --plot file name handing is special as the library wants a
+    filename instead of a stdio file pointer.
+    
+    Add a --all option that just creates all of the possible outputs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 199f0cecae22645140185238682b9e1aba0e5715
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 20:36:20 2009 -0800
+
+    Make TD print 0000-00-00 for invalid dates.
+    
+    This 'shouldn't' happen, but in case it does, I want to know about it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 238d03462a56dc1b7c871608cb00f961a88c1b97
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 20:35:22 2009 -0800
+
+    Make ao-postflight create filenames using input filenames.
+    
+    Instead of requiring the user to provide names for the various output
+    options, just create them from the input name by replacing the extension.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1a3b4c02a01187f8b7b9a9c97712476d0007ab35
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 19:52:26 2009 -0800
+
+    Document ao-postflight --gps and --kml options.
+    
+    These were missing from the man page, but included in the --help output.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 80b23f3044a654d61212891a61fadb8a3a4e5572
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 21 18:57:45 2009 -0800
+
+    Decode HDOP data from skytraq GPS
+    
+    The hdop data was getting dropped on the floor, but is rather useful
+    when deciding if the GPS position is crazy or not. This reports HDOP *
+    5 (giving a useful range of .2-50).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8f036ee7cd30ce3ed2e5cc8501914a4c19b73875
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Nov 20 13:19:02 2009 -0700
+
+    update changelogs for Debian build
+
+commit 87e6f3e5c1688503ab8595912d8d6eb7139830b7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 20 12:16:37 2009 -0800
+
+    Eliminate SiRF state values from ao-view.
+    
+    With Skytraq not having any visible GPS state information, just
+    remove this from the display.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8065b8146a31438e66f83c13b99281ec47439a73
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 20 11:56:48 2009 -0800
+
+    Add GPS date/time output to ao-postflight.
+    
+    GPS date/time information was already being stored in the log, it just
+    wasn't getting displayed by ao-postflight.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6894541e0ee144bfc689cc02d4ed333711d3b500
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Nov 20 11:55:28 2009 -0800
+
+    Reduce igniter firing time from 500ms to 50ms.
+    
+    Given that the system will brown-out with the longer pulse, it doesn't
+    make sense to even try; failure at 50ms probably indicates a short.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b75aa1c825b84bd7fa1578320fbc7e904c373a7d
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Nov 19 21:43:13 2009 -0700
+
+    fix typo in comment
+
+commit c2f661b75ec035f6a3f700962290ef297f9ab0af
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Fri Nov 20 11:44:06 2009 -0700
+
+    update changelogs for Debian build
+
+commit b0d7e3f9c9322542e9b649bb6ad7f7e5bb99dffa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 15 16:20:18 2009 -0800
+
+    Stop using SiRF state info.
+    
+    With the switch to the skytraq GPS unit, we don't have the same level
+    of detail in the GPS stream, so stop reporting that in the telemetry
+    stream, in the UI and writing it to eeprom.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 524665fc221b0d483453c67b7211e282cebc8980
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 15 16:04:41 2009 -0800
+
+    Add date to GPS data, captured from GPRMC packet.
+    
+    Pull the date out of the GPS stream and send it over the telemetry
+    link and write it to the eeprom.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3ee279ba76c2a79d142c466f19ef758cf4c01d70
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 15 15:59:01 2009 -0800
+
+    Add flight number to telemetry stream.
+    
+    This makes it easier to tie the telemetry and eeprom files together as
+    they're now both labeled with serial and flight numbers, which should
+    be unique.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6391c89bd5b89f5f46255b8365c658a873e5959a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 15 15:53:01 2009 -0800
+
+    Switch order of serial/flight in ao-postflight summary
+
+commit 9b06e294e2777f69bcf5e98789c3f5477097d53b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 15 15:51:58 2009 -0800
+
+    Enable telemetry receive in ao_view
+
+commit 4cffc9c4b079e39c8196ddbaf91129cda6df7f8b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 14 22:24:37 2009 -0800
+
+    Share telemetry parsing code in cc library.
+    
+    ao-view had a private copy of the telemetry parsing code which
+    included the ability to parse the newer version of that file. Those
+    changes have been moved to the library version and the private copy removed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0e0db8ca3af4c07bae909938486766c646bf580b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 14 22:24:09 2009 -0800
+
+    Provide a dummy 'uninstall' target in the src directory.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1c654a9369294c9b8066c33f91161d8005b96680
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 14 22:17:24 2009 -0800
+
+    Loosen tolerances for main->landed transition
+    
+    Detecting that the rocket has landed is required for the system to
+    flush the eeprom log and re-enable the RDF beacon. This patch changes
+    the landed state entry requirements for the accelerometer to require
+    only that the accelerometer stay within a quarter of a g (down from
+    1/10g) and changes the testing interval from 20 seconds to 5
+    seconds.
+    
+    The requirement that the barometric altitude be within 1000m of the
+    launch altitude and that the barometer change by no more than 0.05kPa
+    are unchanged.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 03092d1a72a9651711e22c58dca6d6aba5705c5e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Nov 14 16:35:12 2009 -0800
+
+    ao-postflight: fix sloppy gps sat data realloc code (was crashing).
+    
+    Realloc'ing the wrong data, and failing to set the realloc'ed size was
+    causing ao-postflight to crash while reading long logs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit adf656192441eb7f44792955c86e469145477e29
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 5 22:11:59 2009 -0800
+
+    Return radio to telemetry settings when packet system closed.
+    
+    To receive telemetry after disabling the packet system, the radio must
+    be reconfigured for telemetry mode.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 83afdbdc154fe013bfe35ce5ecf1d61570b04ed6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 5 21:45:00 2009 -0800
+
+    Add reboot command.
+    
+    This resets the processor using the watchdog timer.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0358988ac0ee25a564d48af79b1c3fb0c0fe0a88
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 5 21:44:31 2009 -0800
+
+    Add Watchdog Timer Control register definitions
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4114210e0b813f4af99d0cb7755ad2ac2c4b120e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Nov 5 21:43:21 2009 -0800
+
+    Move ao_match_word from ao_ignite.c to ao_cmd.c
+    
+    This is a generally useful command line utility.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5a79a04ddb0b3ee64de34e366f71a0f6db509c01
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 4 21:59:51 2009 -0800
+
+    Stop recording in ao-dumplog after receiving an invalid block
+    
+    If no samples in a block are valid, assume the flight log is over.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 843ee489aac34ad6d81f55f1c85fb9eecc42d86b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 4 21:59:12 2009 -0800
+
+    Flush pending input when switching to remote packet mode
+    
+    Any pending input would just confuse the application, so pull it off
+    the link and dump it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 036400a2db303f3db3be7cc0426f88359c6bd2b1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 4 21:42:51 2009 -0800
+
+    Explicitly use USB I/O routines in packet code
+    
+    Using the implicit stdio functions (putchar/getchar/flush) would
+    result in essentially random redirection of each, depending on whether
+    the packet code had characters available when getchar was called. This
+    would cause lockups in putchar.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3ece984f4d72b4f720a5efdfaad7cff77a93d676
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 4 21:32:07 2009 -0800
+
+    In USB pollchar, wait for packet before re-checking USB out len
+    
+    This probably wouldn't actually cause a problem, but it seems more
+    reliable to wait for a packet interrupt before re-reading the packet
+    OUT len register. This could avoid spinning while waiting for a USB
+    packet, which seems like a good thing.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bc62bb254085cc705203b57260c04ac5e14c6611
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Nov 4 21:29:37 2009 -0800
+
+    In packet master, move USB flush from packet thread to echo thread
+    
+    This keeps the packet thread from blocking on USB and also makes the
+    flush happen after every packet (slightly more USB traffic, but
+    packets are slow anyway).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 27ebaf8e13aed06bb1ea6e770f767495a02be6c5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 3 01:27:37 2009 -0800
+
+    Add ability to dump eeprom data over radio link.
+    
+    This adds a '-R' option to ao-dumplog to redirect the connection
+    through a USB attached TeleDongle over the radio link to a remote
+    TeleMetrum device.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1de322b960005c9a16051afa1881fadb00f4bcd6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Nov 3 00:40:38 2009 -0800
+
+    Pass accel calibration over telemetry stream. Telemetry data format change.
+    
+    This allows the ground station to convert the accelerometer sensor
+    values into acceleration and speed data. This requires a new telemetry
+    data structure, and so TeleMetrum and TeleDongle units must be updated
+    synchronously. ao-view will parse either telemetry stream, and the
+    serial format from TeleDongle now has a version number to allow for
+    future changes.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b529e5e8998702986909111a457f3ce9932e1ccf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 23:48:29 2009 -0800
+
+    ao_flight_test was using accel value for pressure too
+
+commit f57bea012d4fbca097df0d98fcd30eb4abd9701a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 21:47:41 2009 -0800
+
+    Reformat ADC values to show all 16 bits
+
+commit 79718e798e96567f0ba11c61f187e432fdcf95ee
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 21:48:16 2009 -0800
+
+    Remove "f" command
+
+commit a4137263b69864c524d39c6ff88a0225fd06e79b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 21:47:15 2009 -0800
+
+    Remove "d" command
+
+commit 47f510464907d2b9488109c96ade87a41d878842
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 21:46:39 2009 -0800
+
+    Remove "l" command as ao-dumplong no longer uses it
+
+commit 144db05f6b286a0450d486f69ce192632a2c0656
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 21:38:18 2009 -0800
+
+    Add two-point accelerometer calibration.
+    
+    Calibration now uses two values, one upside right and the other upside
+    down instead of a single horizontal value. This allows the use of
+    other accelerometers and compensates for variations in the divider
+    circuit to provide more accurate data.
+
+commit 17611788aadc9460287145a340a7c18bf63766aa
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 2 16:54:06 2009 -0700
+
+    update changelogs for Debian build
+
+commit 7db9d86178ecfd58cc1c17ac9fcbdcfd2f13aaec
+Merge: b219801 f9de200
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 15:47:40 2009 -0800
+
+    Merge remote branch 'origin/master'
+
+commit b219801fb0e5eaff7778d21701da977104522da3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 15:45:58 2009 -0800
+
+    Add ao_usb_pollchar to ao.h
+
+commit b92333ff5e75bf96804359e9fbf464d3b518bd95
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 15:45:42 2009 -0800
+
+    Disable interrupts while removing tasks from task list
+
+commit d519564fd08d2defe1211de83ccbdfa3c7cfe702
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Nov 2 15:45:07 2009 -0800
+
+    Add more docs to the README file
+
+commit f9de20000794c97a04d5bc2476191864bd2af371
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 2 16:00:11 2009 -0700
+
+    update changelogs for Debian build
+
+commit 127c3125e5a4b86b8f304bc2889e313688c3d83e
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 2 15:59:12 2009 -0700
+
+    update changelogs for Debian build
+
+commit 6b1e77569e2fed3c44606ed268421df5d3ed4020
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 2 15:57:41 2009 -0700
+
+    update changelogs for Debian build
+
+commit 8203bfa58af32a1d07a44c1c151b200df0b65f15
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Nov 2 15:56:42 2009 -0700
+
+    de-version the libreadline-dev build dependency
+
+commit 0b483233118673cbc2cda1be6acd379df82bc95a
+Merge: ca5d323 550482d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 1 20:59:02 2009 -0800
+
+    Merge remote branch 'origin/master' into skytraq
+
+commit ca5d323a3d206050d95f52a61e92c69e1f54e7b5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Nov 1 20:57:03 2009 -0800
+
+    Enable packet-based communcation to command processor
+    
+    This splits the packet code into master/slave halves and hooks the
+    slave side up to the getchar/putchar/flush logic in ao_stdio.c
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6c1a9ce16b966a21c885bf3be31cbcb85368b3fa
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 31 01:30:22 2009 -0700
+
+    No need to wakeup &ao_tick_count now
+
+commit cd0d495d7ef276956e730196476daa70a4359918
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 31 01:20:26 2009 -0700
+
+    Poke master to speed up packet rate when things are busy
+
+commit 442b1ef18c8320d4e5329dc92bb5268a36058fc5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 31 01:19:41 2009 -0700
+
+    Use ao_alarm for ao_delay so it can be easily interrupted
+
+commit 4f7ed9ff484778381db647c27d2a34d0cadec41e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:53:03 2009 -0700
+
+    Do more flushing in packet test code
+
+commit 6a7aa1810a90a9216160aec55ec4bd02b3240e1b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:52:44 2009 -0700
+
+    Add RFIM register
+
+commit bf65e0b2a1299b49adc2d339ab9d9c7599aded9e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:52:22 2009 -0700
+
+    Send SYN packet to set sequence numbers
+
+commit d46797e5c08d4955d516458185e2cfb51ee2d567
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:51:38 2009 -0700
+
+    Use ao_radio_done to wait for TX to completely finish with packet
+
+commit c5ec6fcfa1bd17aad0f85d2fbe603f1d125836e6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:50:54 2009 -0700
+
+    Switch packet code from timer thread to ao_alarm
+
+commit b428faf74ae145126ec1da972028fcfe0b4b2b18
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:48:36 2009 -0700
+
+    Remove reason from ao_dma_abort
+
+commit 73db30b2f9128c37dc7fa075793a8862814ce044
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:46:21 2009 -0700
+
+    Add ao_alarm
+
+commit 251b0971f049cbf2f8db79a32729d47441ce65f3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:45:43 2009 -0700
+
+    Wait for TX to finish sending data
+
+commit 9b31f07fe3556896b3e997bba156e30ef5777a80
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 23:43:52 2009 -0700
+
+    Add radio carrier command
+
+commit 7b14c3e609749f4fc00dbd660541375048535218
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 30 16:39:15 2009 -0700
+
+    Initial packet bits. Just testing transmission
+
+commit 690fc263516d8beb6b24e86fbcd6588f42ce4e5c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Oct 21 17:18:49 2009 +0900
+
+    Add keyhole-markup generation for ao-postflight.
+    
+    This lets you see the flight path in googleearth.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b657aa209b9ea3b3efd33a940283b3ba60a169af
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 16 12:59:53 2009 +0900
+
+    Add ao_wake_task and ao_exit
+    
+    ao_wake_task signals a specific task to wake up.
+    ao_exit terminates the current task.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d709a0688eff84e25e24d755850ef045d6b0c3de
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 16 12:56:45 2009 +0900
+
+    Save some DSEG space by marking cmd functions __reentrant
+    
+    __reentrant causes the compiler to place args and locals on the stack
+    instead of in the data segment.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 550482d953c491a5ede9f2d243493afb13289898
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:58:46 2009 -0600
+
+    update changelogs for Debian build
+
+commit 2de548f45d0f50b558acc83f57e1e2fc1223ab92
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:58:19 2009 -0600
+
+    oops, forgot to specify the repo to push to
+
+commit 68008ac11c2735ca53a1b474324df43f2f1d5cdd
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:57:33 2009 -0600
+
+    update changelogs for Debian build
+
+commit 513328ac5f7c25f9ee144ab6befbea60d69eed1a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:56:50 2009 -0600
+
+    automate push of updated and tagged master branch during debian/rules prebuild
+
+commit 67bf7d388a6dd2dbf65575bf4f0423ad355b4f2f
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:54:44 2009 -0600
+
+    update changelogs for Debian build
+
+commit 23bc21a93ccb9f35917f283ac5df6ce0870df71a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:54:16 2009 -0600
+
+    undue damage from partial build
+
+commit 7da56ad8576ef212ffb6cb573bdaf578453e3fe0
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:52:52 2009 -0600
+
+    add support for tagging git repository on each Debian package build
+
+commit 8d4aa4ee54f85f4951cdd7293d58aaa405cfcdc5
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:49:34 2009 -0600
+
+    update changelogs for Debian build
+
+commit 241a860fe856b1dfad6e792736313648300d5c24
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 16:48:43 2009 -0600
+
+    flush repetitive junk out of debian/changelog, and update the prebuild target
+    in debian/rules to put git commit details into the Debian changelog
+
+commit 6c4cdc927b43736b39be29d23ac3dc723f69e4d6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 15:57:19 2009 -0600
+
+    update changelogs for Debian build
+
+commit c57bd7fd2f80e50b0b4c87fccb024ab07c93773d
+Merge: adf8764 2b76572
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Oct 12 15:57:08 2009 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 69b6f6bb465163cf767bb68e0e4a716d8ad2b39c
+Merge: bc77da6 2b76572
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 17:16:21 2009 -0700
+
+    Merge branch 'master' into skytraq
+
+commit 2b765728ce177e26899f6feef00bfdf6aeaf2678
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 17:15:38 2009 -0700
+
+    Add apogee igniter delay.
+    
+    Provide for a delay after apogee before the drogue charge is
+    fired. This allows TM to be used as a back-up altimeter.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bc77da68c9cb7d4cca483eadbbb7e9ccf71c0060
+Merge: 46cccf6 8f7ea3d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 15:09:48 2009 -0700
+
+    Merge branch 'master' into skytraq
+
+commit 8f7ea3de7037f40b0ff462b60d503c19431ae62b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 15:08:14 2009 -0700
+
+    Report igniter continuity in pad/idle mode via beeper
+    
+    one short beep = drogue
+    two short beeps = main
+    three short beeps = both
+    one long warble = neither
+    
+    In idle mode, it does this just once. In pad mode, it keeps testing
+    and reporting.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit adf8764bc4591795ba4e618ccbd6393fc6ce6450
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Oct 10 15:11:23 2009 -0600
+
+    update changelogs for Debian build
+
+commit 541da6f3bbf81be93dfe3c01f7c8cfd757b28a2b
+Merge: dfc73cb 5f26ad6
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Oct 10 15:05:50 2009 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 46cccf62fb40514b5930fcb2ffdaf2735415c764
+Merge: fb8f3fe 5f26ad6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 14:00:03 2009 -0700
+
+    Merge branch 'master' into skytraq
+
+commit 5f26ad663b3f60dddc9d967206e365f45dc4acd1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 13:58:16 2009 -0700
+
+    ao-dumplog: switch to 'e' command, display progress
+    
+    Using the 'e' command allows additional checking of the data,
+    including end-to-end checksums and detection of missing data.
+    
+    Progress is displayed by showing the recorded flight state along with
+    a '.' for each eeprom block read.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fb8f3fee6a1bab1e46d782e84405845cee2dadb4
+Merge: 22856cf b8fc397
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 13:41:00 2009 -0700
+
+    Merge branch 'master' into skytraq
+
+commit b8fc3975bd92037a0cf53b0ff2b0e05ce0ba668f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 13:39:01 2009 -0700
+
+    Send 0-length IN packet to flush USB after full packet
+    
+    USB bulk transfers are a sequence of maximum-sized packets followed by
+    a short packet, which signals the end of the transfer. When the last
+    packet of the transfer would be a full-sized packet, an additional
+    packet of zero length is sent to signal the transfer end.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 22856cf8bb0f5e1f37c9b774132d9ef6934526ed
+Merge: 2f76034 e29961f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 11:44:20 2009 -0700
+
+    Merge branch 'master' into skytraq
+
+commit e29961fdb2a48874c895829880eadbf13e094c0c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Oct 10 11:43:31 2009 -0700
+
+    Add channel menu to ao-view.
+    
+    Sets radio channel when TD is connected, saves selected channel in
+    gconf database.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2f7603490a169df8f18b565db4fa967832ffc9bd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 9 22:48:29 2009 -0700
+
+    Build two versions of TM, one for SiRF, one for SkyTraq
+    
+    This creates two separate images, depending on which GPS unit is
+    connected.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 33b0b6f2f2e07de105619a7b463226d2813152ab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Oct 9 22:02:40 2009 -0700
+
+    Add support for the SkyTraq GPS unit
+    
+    This is a build-time option selected by hacking the Makefile at present.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a3771bfc5ce740f9d89193e9f8b1d7987aa57264
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 6 20:06:00 2009 -0700
+
+    ao-view: fix snd_pcm_open return checking
+    
+    I don't know how this code was supposed to work before...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ac4b8a73848f434999a532eab4665253c267c597
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Oct 6 20:05:36 2009 -0700
+
+    ao-postflight: dump out GPS signal data
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dfc73cba1bee8b121e00e8cba45e7dfaaf79e9d8
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Sep 21 22:46:59 2009 -0700
+
+    update changelogs for Debian build
+
+commit 459ff3d377297f80ee2fba0df0a29ff6603467a1
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Sep 21 11:00:32 2009 -0700
+
+    update changelogs for Debian build
+
+commit 327c64305a59f48ababf19875874a550af6b9cef
+Merge: c8a81a4 74f0fb4
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Mon Sep 21 11:00:22 2009 -0700
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 74f0fb4dd189abc1d5027c64fa5a648a6003285a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 20 13:33:59 2009 -0700
+
+    make bit-banging reset script actually reset
+
+commit 7ea371a09385e2a93199f78685e8cb86793ed104
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 20 13:33:26 2009 -0700
+
+    Add --gps option to ao-postflight
+
+commit bc7ccb339e538a0e6120db0e5c0d9130c565e0dd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 20 13:32:59 2009 -0700
+
+    ao_rawload: Don't reset after we finish loading
+
+commit c8a81a419f7f2331624f90bd6c107a86f6b04451
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Sep 20 09:21:00 2009 -0600
+
+    update changelogs for Debian build
+
+commit df42ccaaf468cdc5d93cbd1c001f58df58419722
+Merge: 0b24e40 078e9cd
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Sep 20 09:19:28 2009 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 078e9cdbdb388b22c6151f76ff0660fc14b8ef55
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Sep 10 11:53:06 2009 -0700
+
+    Plot raw accel data for the motor accel section.
+    
+    This shows a short sequence of accelerometer data without any filtering.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8b485d937ff148848ebda7f9ca6be29bb1de1f16
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 21:02:48 2009 -0700
+
+    Show acceleration only during boost phase.
+    
+    We're interested in motor performance; the rest of the flight is
+    boring, after all.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9e660315e1bd2bf71ab1c0574e895e1f7608a58f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 21:01:44 2009 -0700
+
+    Fix cc_period_make to not get stuck on samples with matching time
+    
+    When two samples have matching times, step to the second one;
+    otherwise, we'll get stuck forever.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 932f1539b38567e565fd484171c13539b1467308
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 20:26:17 2009 -0700
+
+    Color plots, integrate only flight portion of data.
+    
+    Telemetry files have piles of pad data which shouldn't be integrated
+    into the velocity data as it tends to generate huge values from the
+    noise of the sensor.
+    
+    Also make the data lines colored to keep them visually distinct from
+    the rest of the plot image.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9177f5f4e9d832558ddd9ab227c4511f6201e7e5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 18:11:24 2009 -0700
+
+    Update usage and man page for ao-postflight
+
+commit 0b24e4034f93010372a3d084668d10f0e4a2c2e1
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Sep 6 18:01:20 2009 -0600
+
+    update changelogs for Debian build
+
+commit a5e94aa0677070a051714443cf7fd7e2b5e90269
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Sep 6 17:59:47 2009 -0600
+
+    need a run-time dependency to pull in the cairo module
+
+commit 97acef95cc9843998963921459fdd71dd7eaa6b4
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 20:47:41 2009 -0600
+Date:   Sun Sep 6 17:48:23 2009 -0600
 
-    deliver sources.list.d fragment so updates are closer to automatic
+    update changelogs for Debian build
+
+commit 3f95a5abbf8ada70328ced45fbb2781ed1cb3d29
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Sep 6 17:48:10 2009 -0600
+
+    more build deps for plotting lib
+
+commit 9d7e96e323d652de08b2f2a73e0eb3c321080185
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Sep 6 17:47:56 2009 -0600
+
+    update changelogs for Debian build
 
-commit cec78502c06f11880366f6131c0442906c6ed864
+commit ae4e131b61244e06020b82919e31e05dd7dba88f
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 20:17:31 2009 -0600
+Date:   Sun Sep 6 17:46:39 2009 -0600
 
     update changelogs for Debian build
 
-commit 82d24bc65af85231b0d67394b30adfdb7beb6564
+commit 37e6c9a492a1d51373bf9333fb3172e0c377720f
+Merge: d256f82 2e6686b
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 20:17:08 2009 -0600
+Date:   Sun Sep 6 17:46:10 2009 -0600
 
-    we need ChangeLog in the repo for git-buildpackage to work right
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 2e6686b1e183c66188ea447b8a54e4c29402443b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 16:45:47 2009 -0700
+
+    Use plplotd instead of plplotd-gnome2
 
-commit c8e04df1423bfd4e9318d9fccae2cfb4a97983ab
+commit d256f8204e9fce53ae4309562bb4c0cde1fae43e
+Merge: 0fc344d 32d3536
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 20:12:44 2009 -0600
+Date:   Sun Sep 6 17:34:08 2009 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 32d3536706324808df6cd02248a236302b831571
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 16:24:35 2009 -0700
 
-    update Debian changelog for build
+    Add plots to ao-postflight using the plplot library
+    
+    It's not perfect, but it generates .svg plot output.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 2fda90c0f8ab46a83175ed168fcba9b11efcd453
+commit 0fc344dfc031a8b1eef7cc40efb1d5ba7782269d
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 20:12:22 2009 -0600
+Date:   Sun Sep 6 14:15:57 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit a40f45bf4791bced48efb728be8990e41a7e4280
+commit 4b0de757874c0ecaf38e3dfd3beefc398150e3d5
+Merge: 773c4ff d0eac98
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 20:08:10 2009 -0600
+Date:   Sun Sep 6 14:15:53 2009 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit d0eac989b1ffc8ae30ba12da403eb4bf1ad42d6b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 13:15:10 2009 -0700
 
-    move the ChangeLog creation to the prebuild target
+    Don't look at NULL strings (summary_name)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 73f4a57239f770aff603b961169c0e2cfe2c276b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 13:08:54 2009 -0700
+
+    Use pressure speed for drogue and beyond states. Fix differentiation time.
+    
+    Drogue state should always use pressure speeds.
+    
+    Differentiation code was using centi-seconds instead of seconds.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit ae7752210a0e06beacad011c17dbf75bff39eda8
+commit 773c4ffbc1d2e02eb02cfa543a077a408986da30
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:57:56 2009 -0600
+Date:   Sun Sep 6 14:05:55 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit c7f7a1ed3494db008b397a613cfcd3ab36c01920
+commit 45ede4a4b203ef9da5bf05c49cb9c5a2e6382ec5
+Merge: 45e2938 e35e485
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:56:20 2009 -0600
+Date:   Sun Sep 6 14:05:51 2009 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
 
-    update Debian changelog for build
+commit e35e485ffe6b26034788ab295121bc2693b7eec1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Sep 6 13:04:31 2009 -0700
+
+    Initialize summary_name and detail_name so stuff appears on stdout.
+    
+    Uninitialized variables lead to mysterious results.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit ebaf8b0d02d82bce7c7ea78e281377768b996d6d
-Merge: 79501a5 d996aa9
+commit 45e2938121411d1fc9b3aec3fdeaaeb3c90db5ed
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:56:16 2009 -0600
+Date:   Sun Sep 6 14:02:14 2009 -0600
 
-    Merge branch 'master' into debian
+    update changelogs for Debian build
 
-commit d996aa9b32fb0eb385bd3d158256c29788a42fe3
-Merge: b3b2d3c 7d4ceb7
+commit d42ebf0661ecf15455e5051de1e16ae66f8dd857
+Merge: 384dbe9 7a19aac
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:56:09 2009 -0600
+Date:   Sun Sep 6 14:02:09 2009 -0600
 
     Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
 
-commit 7d4ceb75a454e6c9b3fe0bd934fadcb5104dea36
+commit 7a19aac5e881e635962a64fff73027ca2143b96f
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 17:55:22 2009 -0700
+Date:   Sun Sep 6 12:51:48 2009 -0700
 
-    Add ao-ejection.1 man page
+    Add DSP code to filter data, allowing for integration/differentiation
     
-    Document the input requirements and output format
+    This adds the computation of speed from both accelerometer and
+    barometer measurements and then presents a periodic flight profile
+    using filtered data as a detailed flight record.
     
     Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 79501a5462ab29d661cf76f63628bd4616b6ae1b
-Merge: f48eb20 b3b2d3c
+commit 384dbe9fc7fa8e4e5dceec5e150f0f1d3383bbdc
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:37:10 2009 -0600
+Date:   Sun Sep 6 10:40:06 2009 -0600
 
-    Merge branch 'master' into debian
+    update changelogs for Debian build
 
-commit b3b2d3c475a135084b5628c730fc6fca1ba0817b
-Merge: 4685fc5 da12b89
+commit 35c54b3a278fa9bc2bc7f4b5ee04866697c93ba0
+Merge: 4f8eff7 6d018ab
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:36:03 2009 -0600
+Date:   Sun Sep 6 10:39:23 2009 -0600
 
     Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
 
-commit f48eb2033f3685fedd42d307963671a8060604c3
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:33:53 2009 -0600
-
-    don't ship a zero-length ChangeLog, when it's easy to make one...
-
-commit da12b89fb056a68e65ba363fef91d266727cb685
+commit 6d018ab933832e2d80bb1564c339d9fb18b57be2
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 17:30:43 2009 -0700
+Date:   Sat Sep 5 22:45:49 2009 -0700
 
-    Create ChangeLog from git log
+    Handle vageries of .telem files in ao-postflight
+    
+    Telem files have multiple entries of the same state, and sometimes
+    long gaps between recordings. Deal with this as best as possible.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 7d69e2b3715faed10ce21ad562fc4d25dfc5f9c1
+commit c46e832b28820d7c5be4efaacbbd7c0607927fe5
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 17:29:29 2009 -0700
+Date:   Sat Sep 5 22:03:31 2009 -0700
 
-    Fix ao-bitbang examples to not have . in the first column
-
-commit 192b08c28719b5518ed349d9897f9a21add3c615
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:24:50 2009 -0600
-
-    update Debian changelog for build
+    Add simple post-flight analysis tool (ao-postflight)
+    
+    This tool reads either an eeprom or telem log file and displays some
+    rudimentary data (max accel/alt for each flight stage).
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 5969d1d0e3aa554187a5bcc899a2f1347656fd5e
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:24:22 2009 -0600
+commit 26f56b51bd11aa91f1d77b81827b49c28cb6ec5f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Sep 5 00:29:26 2009 -0700
 
-    fixing things lintian points out
+    Add ao-dumplog to capture flight log from command line
+    
+    This duplicates the functionality of the flight log stuf in ao-view,
+    except from the command line where it belongs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 9bf7c23f6e8ac7080240b91a28b355e50ecb4c4d
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:14:02 2009 -0600
+commit 73adae3661160d410dcc802873b530d255c210e5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 4 15:30:22 2009 -0700
 
-    update Debian changelog for build
+    Add --device/-D support to the command line tools and manuals
+    
+    Use the new cc_usbdevs_find_by_arg function to locate suitable target
+    devices connected via USB.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit d861218bc6c2f57816953af3271d93fc48f8afc2
+commit 4f8eff7401ee2d8092ab36fa33411f9b23dda880
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:13:48 2009 -0600
+Date:   Fri Sep 4 16:03:55 2009 -0600
 
-    fix merge conflict
+    update changelogs for Debian build
 
-commit 3f953f56fbfd4e903c09ca1e4309664d38c2c669
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:10:06 2009 -0600
+commit 332b056459b1352e233a8bf5f08498df12d32160
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 4 15:01:32 2009 -0700
 
-    update Debian changelog for build
+    'fix' ao-eeprom to read two blocks at once. Work around kernel bugs.
+    
+    The kernel appears to leave serial data undelivered at times. Reading
+    two blocks at once appears to make it relinquish the queued data.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 7fcbe76ce82ec5a17cf536afa0d1b9061e225aa9
-Merge: 035ba6d 4685fc5
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:09:59 2009 -0600
+commit df88ae4c5f229efcc0ea5cb0a81fc2bb8f96fea2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 4 14:23:02 2009 -0700
 
-    Merge branch 'master' into debian
+    Add 'ao-list' utility to show attached AltOS devices
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 4685fc541466afbeefc151bcb64cd054739c048b
-Merge: 1c2a0b6 c29275b
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 18:09:38 2009 -0600
+commit 0935d6a7e907e20381a42882ae728051f9bece02
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Sep 4 14:21:19 2009 -0700
 
-    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+    Parse the USB serial number as an integer.
     
-    Conflicts:
-       ao-tools/ao-bitbang/Makefile.am
-       ao-tools/ao-eeprom/Makefile.am
-       ao-tools/ao-load/Makefile.am
-       ao-tools/ao-load/ao-load.c
-       ao-tools/ao-rawload/Makefile.am
+    AltOS devices use simple integer serial numbers, so parse the USB
+    value as such to make matching values more forgiving.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit c29275b72438637d46d7a50742882d2736eb176a
+commit 0c771d999914f9d17c723900f2987acc45fd0fbb
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 15:21:57 2009 -0700
+Date:   Fri Sep 4 13:00:02 2009 -0700
 
-    Add manual pages for remaining commands.
+    Move usb scanning code to ao-tools library
     
-    Manuals written for ao-bitbang, ao-eeprom, ao-load, ao-rawload and
-    ao-view.
+    This will allow the scanning code to be used by the command line tools
+    as well as the ao-view GUI.
     
-    Manual for ao-dbg updated to reflect program name change.
+    Now that ao-view depends on the ao-tools library, it has been moved to
+    the ao-tools directory as well.
     
     Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 9b03d620722dc54630539afba40720c30de69b2d
+commit 15341b6e6dcf52df083d6aa37ef881ea6ad48ee5
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 12:19:31 2009 -0700
+Date:   Fri Sep 4 12:25:37 2009 -0700
 
-    Use --tty/-T on command line to specify target device
+    Set all of the values to reset the radio for telemetry
     
-    Also, use the ALTOS_TTY environment variable in all tools. Note that
-    the magic value of "BITBANG" switches the library to connecting
-    through a CP2103 instead.
+    Was sizeof(rdf_setup) instead of sizeof(telemetry_setup) when
+    resetting the radio back to telemetry data mode from rdf mode. With
+    the length value removed from the rdf config, these two arrays are no
+    longer the same length, and so the last config value was not set
+    leaving the radio sending garbage.
     
     Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 7c790fe859dff062692964338091ffbbcdf63257
+commit fee46389b70a624ab5b1128a8b4c3083c7747bcb
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 12:40:24 2009 -0700
+Date:   Fri Sep 4 11:46:55 2009 -0700
 
-    Rename tools to ao-<foo>
+    Make RDF beacon only run on pad and after landing.
     
-    Use a consistent prefix to make it easier to remember which programs
-    belong to this package
+    It's pretty much impossible to RDF the rocket during flight, and it
+    interferes with the telemetry data stream. Leave it enabled on the pad
+    so that radios can be tested, and then re-enable it once the rocket
+    has landed.
+    
+    This patch also turns the rdf 'on' time into a parameter so it can be
+    changed, and then sets that parameter to 500ms, once every 5 seconds.
     
     Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 035ba6df17f016953351bc77a98623c165b69909
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 14:40:58 2009 -0600
-
-    update Debian changelog for build
-
-commit 7aa251970cbbb6b9d9678ed0721e28da3df9036b
-Merge: 107055e 1c2a0b6
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 14:40:54 2009 -0600
-
-    Merge branch 'master' into debian
-
-commit 1c2a0b6653623b689d68d7349a6b2dce3e20a4a6
+commit 54545640b0db7747137655f84bc67fd290ecb904
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 13:36:54 2009 -0700
+Date:   Fri Sep 4 11:45:52 2009 -0700
 
-    re-add debugger sources
+    Add back the RDF tone generator
+    
+    Tracking the rocket on the ground may be easier using tones than using
+    the digital data stream, so we'll try that and see what we think.
+    
+    This reverts commit 3a3bfd471a868d546d83cdc431b53c8f5208edb9.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 107055e969e89771d1e2f8ac8b1a4c5b4b9e9a48
+commit 9fafee109e96435c96639b21211cac0500673a63
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 14:03:48 2009 -0600
+Date:   Wed Sep 2 23:18:15 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit 14915158049918fa2fa5294f325b77dc9b08bab8
+commit cb4a73f3b65ba72f645fd37ab8712829c9537bf8
+Merge: 9ddd869 e2e449d
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 14:03:41 2009 -0600
+Date:   Wed Sep 2 23:17:37 2009 -0600
 
-    update Debian changelog for build
+    Merge commit 'origin/master'
 
-commit 0fcc426f96577ebbaf0c2d009cd2708e974de315
-Merge: 91b8592 9789ca5
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 14:03:16 2009 -0600
+commit e2e449d5c23356e913f312de1fb2611a9dd5a352
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Sep 2 22:01:52 2009 -0700
 
-    Merge branch 'master' into debian
+    Remove bit-banging debug support from TM board builds
+    
+    Our current TM boards don't have the wires to do bit-banging to
+    another cc1111 board, so it doesn't make sense to fill up their flash
+    with useless code (and the 'help' text with useless commands). Leave
+    this to the TI board until we have boards that can serve as debug dongles.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 9a9cce5510b87252f863239ac807b9fb4395b288
+commit acea083d80e1ecc4287083519ea666964016b257
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 12:19:31 2009 -0700
+Date:   Wed Sep 2 22:00:37 2009 -0700
 
-    Start working on using getopt for the tty name
+    Make the ao_log_record structures 8 bytes again.
+    
+    When the GPS signal strength data was added, the structure was
+    accidentally extended to 9 bytes, making all log records 9 bytes
+    long. While not a serious problem, this left log records spanning
+    across eeprom block boundaries, which seems like a bad plan.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 9789ca5e8caa9a013e804f307b9da380e147bd75
+commit 7d39f17684feb49ac8a0017902158f298696e37c
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 12:40:24 2009 -0700
+Date:   Wed Sep 2 21:57:54 2009 -0700
 
-    Rename tools to ao-<foo>
+    Make eeprom reads and writes across block boundary work
     
-    Use a consistent prefix to make it easier to remember which programs
-    belong to this package
+    Reading and writing across the block boundary was not stepping the
+    eeprom position after the partial i/o operation at the end of the
+    first block. This meant that the operation would re-use the end of the
+    previous block, either re-reading or re-writing it.
     
     Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 91b85929df6a3464396702fd177a6f74c6f28c7a
+commit 9ddd8696b4004ccc03238d95a8c2a1d07075e0fb
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:59:36 2009 -0600
+Date:   Mon Aug 31 16:48:03 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit 645cc909a279347aa1665ce883c71090b29935a9
+commit 6926c4ab5d87a8f2eb4fcde2c673fb3a4639e115
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:59:28 2009 -0600
+Date:   Mon Aug 31 16:47:44 2009 -0600
 
-    update Debian changelog for build
+    pixmap file should not be executable
 
-commit f54e075705b3d4d6919494e8cff99141128f0303
+commit 1495e2f27acde3743c3764a0c31ee082224d6c64
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:59:15 2009 -0600
+Date:   Mon Aug 31 16:42:55 2009 -0600
 
-    deliver firmware files too
+    update changelogs for Debian build
 
-commit 30fb0e78a464794a11fb8cf4ae385c3123922371
+commit c8c5b7963babe8eb16e2651fba9cd2c8d1cba74e
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:52:39 2009 -0600
+Date:   Mon Aug 31 16:42:37 2009 -0600
 
-    update Debian changelog for build
+    deliver an icon for the Debian menu system
 
-commit 022e77db3416175e9b440828f94fb75b739ec00a
+commit 591b99c232e780246fc07841c09c8c4e7835facb
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:49:05 2009 -0600
+Date:   Mon Aug 31 16:26:00 2009 -0600
 
-    oops, need to force prefix to /usr for Debian policy compliance
+    update changelogs for Debian build
 
-commit a382039ebb91e3bea682b601feef15517290ad3b
+commit b34474c1f3083e73b7184d519f54d4c8031836fd
+Merge: 8df1697 0d65bff
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:39:41 2009 -0600
+Date:   Mon Aug 31 16:25:32 2009 -0600
 
-    update Debian changelog for build
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
 
-commit 90347b16e11743e80f85308700e50b1228c2b8e0
-Merge: 06a0e43 a578239
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:39:17 2009 -0600
+commit 6358041f846ba9a20fa650c367d907dc4336e54c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Aug 22 13:38:56 2009 -0700
 
-    Merge branch 'master' into debian
+    Enable GPS degraded mode, set 10 sec degraded timeout.
+    
+    No reason not to let the GPS report solutions whenever it likes, let's
+    see how this works.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit a5782398d968e7cb11f7203afada7c216f233b3b
+commit 0d65bff443c17d4d98c18b620ec075ab66b30efd
 Author: Keith Packard <keithp@keithp.com>
-Date:   Tue Aug 18 11:34:28 2009 -0700
+Date:   Fri Aug 21 10:47:46 2009 -0700
 
-    Remove unused cctools code paths for old libusb and cp2103 ioctls.
-    
-    Communication with the CP2103 board has gone through three revisions,
-    first using ioctls supported by the CP2103 kernel driver, then using
-    the old synchronous usb library and now using the newer libusb
-    asynchronous interface. There's no reason to keep shipping the old
-    stale code now that the new stuff works reliably.
+    Turn off GPS tracking data when not present in data stream
     
     Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 06a0e432480efb4e10220b0bdf402282aad32de9
+commit 8df169791835510d96c11a3b0aa3cc5b79fa7fde
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:16:23 2009 -0600
+Date:   Wed Aug 19 02:21:23 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit f9126a21b6a027e56d3c9d086889375fd7f37f3c
+commit 42ab6d52540d0326ef89e9d57954b08248558468
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:06:03 2009 -0600
+Date:   Wed Aug 19 02:21:06 2009 -0600
 
-    update Debian changelog for build
+    fix location for delivery of sources.list fragment
 
-commit 4d7c4c69a5e0ab16018c2a9325168e363a10084b
+commit 977f5dc0bc7c666dcc1f21db77416efca0d696aa
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 12:05:46 2009 -0600
+Date:   Wed Aug 19 02:11:11 2009 -0600
 
-    fix the changelog version extraction to work for a native version
+    update changelogs for Debian build
 
-commit fc761cc0117cb5a091b041b73b34d222b56a60f0
+commit 8d4d6655f1b4c2fbc522fd255bfb75406e5ddaef
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:58:29 2009 -0600
+Date:   Wed Aug 19 02:11:00 2009 -0600
 
-    update Debian changelog for build
+    oops, aoview is now ao-view!
 
-commit 9a97abb7b1afc90e8f67ba065583a49107a55f61
+commit b9a97aea65f871fd287bc0bb566d8664766f4afd
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:58:18 2009 -0600
+Date:   Wed Aug 19 02:07:16 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit 24c337de950ac5d4b1d074dc20eb7c6a68338462
+commit 21e3dd0affac89919e5d0e29c6e9eb1eacb51801
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:58:11 2009 -0600
+Date:   Wed Aug 19 02:06:49 2009 -0600
 
-    oops, fix syntax
+    enable support for Debian menus
 
-commit 11c610c0e09d4f175b7e164e071ada361a1e9e2e
+commit 0087c1776e0253fc2bd3b86f15bf9d1b32bdc45a
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:57:52 2009 -0600
+Date:   Wed Aug 19 00:52:57 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit f65fa2b7d810ebae4b6df313de8bfe3687223a16
+commit 4486d9156e19e4280b42bcd422d81d04f2d04a92
+Merge: dd09f0b 33edd62
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:57:00 2009 -0600
+Date:   Wed Aug 19 00:49:24 2009 -0600
 
-    call debian/rules prebuild before creating source tarball
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
 
-commit dc5679ee90429fe43c6180d1d298183a79334dfc
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:54:11 2009 -0600
+commit 33edd62992a32b0ec8ca66d879fa300871db5937
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 23:38:16 2009 -0700
 
-    update Debian changelog for build
+    Update ao-view to add GPS satellite tracking data
+    
+    This adds another column to the display to hold per-satellite GPS
+    tracking data and a count of the visible and locked sats.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit cf1fa8b062c05ee995d6befee9088908b2b22473
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:53:31 2009 -0600
+commit 29687cbd462a332d9a36ed87500c5b737dcae3f4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 22:35:15 2009 -0700
 
-    add a prebuild target for use from git-buildpackage clean hook, that creates
-    a new debian/changelog entry based on git-describe output
+    Handle GPS satellite tracking data
+    
+    SiRF message #4 includes signal strength and GPS engine state for each
+    of the satellites being tracked. This data is now parsed and sent to
+    eeprom and the radio.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit d7e60e48b6d85274690478223ef3b571a818e228
+commit dd09f0bc2b950c00f3b489878cd69ad8a003f46c
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 11:52:46 2009 -0600
+Date:   Tue Aug 18 21:57:01 2009 -0600
 
-    update Debian changelog for build
+    update changelogs for Debian build
 
-commit e946fa231a719abfdd3f319b8c3345dfc75ee185
+commit cd5ce661e2a8f9694933358ccb5b916fbed089c2
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 00:08:30 2009 -0600
+Date:   Tue Aug 18 21:49:39 2009 -0600
 
-    allow pass-in of VERSION, only call git describe if VERSION not set yet
+    add support for building Debian package
 
-commit f762a9dfb3b9c81e443a85cd434154598c737ed8
+commit d996aa9b32fb0eb385bd3d158256c29788a42fe3
+Merge: b3b2d3c 7d4ceb7
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 00:06:24 2009 -0600
+Date:   Tue Aug 18 18:56:09 2009 -0600
 
-    see if this works
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
 
-commit c6514ac5c47b44456a15d0f627f274eca769122e
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Tue Aug 18 00:03:27 2009 -0600
+commit 7d4ceb75a454e6c9b3fe0bd934fadcb5104dea36
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 17:55:22 2009 -0700
 
-    undo the post-commit hook stuff
+    Add ao-ejection.1 man page
+    
+    Document the input requirements and output format
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-commit 2d53e72e938086688c81d16750b2c880fee1f4cd
+commit b3b2d3c475a135084b5628c730fc6fca1ba0817b
+Merge: 4685fc5 da12b89
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Mon Aug 17 23:55:47 2009 -0600
+Date:   Tue Aug 18 18:36:03 2009 -0600
 
-    tweak makefile to use version file instead of calling git describe directly
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
 
-commit 2732624c67f5397fed5f31b062feb0f27b7f8fbd
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Mon Aug 17 23:53:35 2009 -0600
+commit da12b89fb056a68e65ba363fef91d266727cb685
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 17:30:43 2009 -0700
 
-    add version to the list of ignored files
+    Create ChangeLog from git log
 
-commit 08b28a0d4a71ba25ca949fe57bb150a89b401cef
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Mon Aug 17 23:39:35 2009 -0600
+commit 7d69e2b3715faed10ce21ad562fc4d25dfc5f9c1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 17:29:29 2009 -0700
 
-    add build dependencies
+    Fix ao-bitbang examples to not have . in the first column
 
-commit bc5d65b7e9776c87948edc4bd88fc4125ca20209
+commit 4685fc541466afbeefc151bcb64cd054739c048b
+Merge: 1c2a0b6 c29275b
 Author: Bdale Garbee <bdale@gag.com>
-Date:   Mon Aug 17 23:32:49 2009 -0600
+Date:   Tue Aug 18 18:09:38 2009 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+    
+    Conflicts:
+       ao-tools/ao-bitbang/Makefile.am
+       ao-tools/ao-eeprom/Makefile.am
+       ao-tools/ao-load/Makefile.am
+       ao-tools/ao-load/ao-load.c
+       ao-tools/ao-rawload/Makefile.am
 
-    build as a Debian specific package since we have no "upstream"
+commit c29275b72438637d46d7a50742882d2736eb176a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 15:21:57 2009 -0700
 
-commit e4fb67c989e10f626042e3b125ffd900bf477d60
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Mon Aug 17 23:31:52 2009 -0600
+    Add manual pages for remaining commands.
+    
+    Manuals written for ao-bitbang, ao-eeprom, ao-load, ao-rawload and
+    ao-view.
+    
+    Manual for ao-dbg updated to reflect program name change.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-    add a config file for git-buildpackage
+commit 9b03d620722dc54630539afba40720c30de69b2d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 12:19:31 2009 -0700
 
-commit 5f59df632f3a82348aefc2b16f2521f6ebd30a50
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Mon Aug 17 23:31:20 2009 -0600
+    Use --tty/-T on command line to specify target device
+    
+    Also, use the ALTOS_TTY environment variable in all tools. Note that
+    the magic value of "BITBANG" switches the library to connecting
+    through a CP2103 instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
-    don't fail to clean if Makefile not present
+commit 7c790fe859dff062692964338091ffbbcdf63257
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 12:40:24 2009 -0700
 
-commit 946f2be3f4fdd0e7cf5c9867ff6fa0d348d95ee1
-Author: Bdale Garbee <bdale@gag.com>
-Date:   Mon Aug 17 23:30:20 2009 -0600
+    Rename tools to ao-<foo>
+    
+    Use a consistent prefix to make it easier to remember which programs
+    belong to this package
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1c2a0b6653623b689d68d7349a6b2dce3e20a4a6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 13:36:54 2009 -0700
 
-    package for Debian
+    re-add debugger sources
+
+commit 9a9cce5510b87252f863239ac807b9fb4395b288
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 12:19:31 2009 -0700
+
+    Start working on using getopt for the tty name
+
+commit 9789ca5e8caa9a013e804f307b9da380e147bd75
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 12:40:24 2009 -0700
+
+    Rename tools to ao-<foo>
+    
+    Use a consistent prefix to make it easier to remember which programs
+    belong to this package
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a5782398d968e7cb11f7203afada7c216f233b3b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Aug 18 11:34:28 2009 -0700
+
+    Remove unused cctools code paths for old libusb and cp2103 ioctls.
+    
+    Communication with the CP2103 board has gone through three revisions,
+    first using ioctls supported by the CP2103 kernel driver, then using
+    the old synchronous usb library and now using the newer libusb
+    asynchronous interface. There's no reason to keep shipping the old
+    stale code now that the new stuff works reliably.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
 
 commit 7cba411de0780c65e3490ab67186a514f0ea42ec
 Author: Keith Packard <keithp@keithp.com>
diff --git a/INSTALL b/INSTALL
index 8b82ade08e87bbfb02fbf4c3b45b181ab6dda8cb..7d1c323beae76333f523f6df31c47a87f5597edb 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -2,10 +2,12 @@ Installation Instructions
 *************************
 
 Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
-2006, 2007, 2008 Free Software Foundation, Inc.
+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
-   This file is free documentation; the Free Software Foundation gives
-unlimited permission to copy, distribute and modify it.
+   Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved.  This file is offered as-is,
+without warranty of any kind.
 
 Basic Installation
 ==================
@@ -13,7 +15,11 @@ Basic Installation
    Briefly, the shell commands `./configure; make; make install' should
 configure, build, and install this package.  The following
 more-detailed instructions are generic; see the `README' file for
-instructions specific to this package.
+instructions specific to this package.  Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below.  The lack of an optional feature in a given package is not
+necessarily a bug.  More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
 
    The `configure' shell script attempts to guess correct values for
 various system-dependent variables used during compilation.  It uses
@@ -42,7 +48,7 @@ may remove or edit it.
 you want to change it or regenerate `configure' using a newer version
 of `autoconf'.
 
-The simplest way to compile this package is:
+   The simplest way to compile this package is:
 
   1. `cd' to the directory containing the package's source code and type
      `./configure' to configure the package for your system.
@@ -53,12 +59,22 @@ The simplest way to compile this package is:
   2. Type `make' to compile the package.
 
   3. Optionally, type `make check' to run any self-tests that come with
-     the package.
+     the package, generally using the just-built uninstalled binaries.
 
   4. Type `make install' to install the programs and any data files and
-     documentation.
-
-  5. You can remove the program binaries and object files from the
+     documentation.  When installing into a prefix owned by root, it is
+     recommended that the package be configured and built as a regular
+     user, and only the `make install' phase executed with root
+     privileges.
+
+  5. Optionally, type `make installcheck' to repeat any self-tests, but
+     this time using the binaries in their final installed location.
+     This target does not install anything.  Running this target as a
+     regular user, particularly if the prior `make install' required
+     root privileges, verifies that the installation completed
+     correctly.
+
+  6. You can remove the program binaries and object files from the
      source code directory by typing `make clean'.  To also remove the
      files that `configure' created (so you can compile the package for
      a different kind of computer), type `make distclean'.  There is
@@ -67,8 +83,15 @@ The simplest way to compile this package is:
      all sorts of other programs in order to regenerate files that came
      with the distribution.
 
-  6. Often, you can also type `make uninstall' to remove the installed
-     files again.
+  7. Often, you can also type `make uninstall' to remove the installed
+     files again.  In practice, not all packages have tested that
+     uninstallation works correctly, even though it is required by the
+     GNU Coding Standards.
+
+  8. Some packages, particularly those that use Automake, provide `make
+     distcheck', which can by used by developers to test that all other
+     targets like `make install' and `make uninstall' work correctly.
+     This target is generally not run by end users.
 
 Compilers and Options
 =====================
@@ -93,7 +116,8 @@ same time, by placing the object files for each architecture in their
 own directory.  To do this, you can use GNU `make'.  `cd' to the
 directory where you want the object files and executables to go and run
 the `configure' script.  `configure' automatically checks for the
-source code in the directory that `configure' is in and in `..'.
+source code in the directory that `configure' is in and in `..'.  This
+is known as a "VPATH" build.
 
    With a non-GNU `make', it is safer to compile the package for one
 architecture at a time in the source code directory.  After you have
@@ -120,7 +144,8 @@ Installation Names
    By default, `make install' installs the package's commands under
 `/usr/local/bin', include files under `/usr/local/include', etc.  You
 can specify an installation prefix other than `/usr/local' by giving
-`configure' the option `--prefix=PREFIX'.
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
 
    You can specify separate installation prefixes for
 architecture-specific files and architecture-independent files.  If you
@@ -131,15 +156,46 @@ Documentation and other data files still use the regular prefix.
    In addition, if you use an unusual directory layout you can give
 options like `--bindir=DIR' to specify different values for particular
 kinds of files.  Run `configure --help' for a list of the directories
-you can set and what kinds of files go in them.
+you can set and what kinds of files go in them.  In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+   The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+   The first method involves providing an override variable for each
+affected directory.  For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'.  Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated.  The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+   The second method involves providing the `DESTDIR' variable.  For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names.  The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters.  On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
 
    If the package supports it, you can cause programs to be installed
 with an extra prefix or suffix on their names by giving `configure' the
 option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
 
-Optional Features
-=================
-
    Some packages pay attention to `--enable-FEATURE' options to
 `configure', where FEATURE indicates an optional part of the package.
 They may also pay attention to `--with-PACKAGE' options, where PACKAGE
@@ -152,6 +208,13 @@ find the X include and library files automatically, but if it doesn't,
 you can use the `configure' options `--x-includes=DIR' and
 `--x-libraries=DIR' to specify their locations.
 
+   Some packages offer the ability to configure how verbose the
+execution of `make' will be.  For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
 Particular systems
 ==================
 
@@ -159,7 +222,7 @@ Particular systems
 CC is not installed, it is recommended to use the following options in
 order to use an ANSI C compiler:
 
-     ./configure CC="cc -Ae"
+     ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
 
 and if that doesn't work, install pre-built binaries of GCC for HP-UX.
 
@@ -174,6 +237,16 @@ and if that doesn't work, try
 
      ./configure CC="cc -nodtk"
 
+   On Solaris, don't put `/usr/ucb' early in your `PATH'.  This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'.  So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+   On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'.  It is recommended to use the following options:
+
+     ./configure --prefix=/boot/common
+
 Specifying the System Type
 ==========================
 
@@ -189,7 +262,8 @@ type, such as `sun4', or a canonical name which has the form:
 
 where SYSTEM can have one of these forms:
 
-     OS KERNEL-OS
+     OS
+     KERNEL-OS
 
    See the file `config.sub' for the possible values of each field.  If
 `config.sub' isn't included in this package, then this package doesn't
@@ -277,7 +351,7 @@ operates.
      `configure' can determine that directory automatically.
 
 `--prefix=DIR'
-     Use DIR as the installation prefix.  *Note Installation Names::
+     Use DIR as the installation prefix.  *note Installation Names::
      for more details, including other options available for fine-tuning
      the installation locations.
 
index 96b9dea91d87f110abcc60149ada5d79e1b529f6..aaa0ae14c7a4029a3b218f23b58ff38025a604e9 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS=src ao-view ao-tools ao-utils
+SUBDIRS=src doc altoslib altosui ao-tools ao-utils altosdroid
 
 EXTRA_DIST = ChangeLog
 
@@ -11,3 +11,9 @@ ChangeLog:
        (touch ChangeLog; echo 'git directory not found: installing possibly empty changelog.' >&2)
 
 dist-hook: ChangeLog
+
+fat:
+       cd src && $(MAKE) all
+       cd doc && $(MAKE) all
+       cd altosui/libaltos && $(MAKE) all
+       cd altosui && $(MAKE) fat
diff --git a/README b/README
index 1244f8492d5db6566fc79cd8a02fe5bb8b0bd61f..282b9ce5a95d63139a9f3d0fdadb71c31884f831 100644 (file)
--- a/README
+++ b/README
@@ -17,18 +17,18 @@ Copyright and License
        with this program; if not, write to the Free Software Foundation, Inc.,
        59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 
-Parameters:
+Tasking API:
 
- * Multi-tasking
- * Non-preemptive
- * Unix-style sleep/wakeup scheduling
- * Strict round-robin, no priorities
       * Multi-tasking
       * Non-preemptive
       * Unix-style sleep/wakeup scheduling
       * Strict round-robin, no priorities
 
-API:
+       uint8_t ao_sleep(void *wchan)
 
-       void ao_sleep(void *wchan)
-
-               Puts current task to sleep. Will wake up when wchan is signalled
+               Puts current task to sleep. Will wake up when wchan is
+               signalled (returning 0), or when an alarm has expired
+               (returning 1).
        
        void ao_wakeup(void *wchan)
 
@@ -52,3 +52,63 @@ API:
 
                Any fatal error should call this function, which can
                display the error code in some cryptic fashion.
+
+       void ao_wake_task(struct ao_task *task)
+
+               Wake the task as if the wchan it is waiting on had
+               been signalled.
+
+       void ao_alarm(uint16_t delay)
+
+               Queue an alarm to expire 'delay' ticks in the future.
+               The alarm will cause the task to wake from ao_sleep
+               and return 1. Alarms are cancelled if the task is
+               awoken by ao_wakeup or ao_wake_task.
+
+       void ao_exit(void)
+
+               Stop the current task and remove it from the list of
+               available tasks.
+
+Timer API
+
+        * Regular timer ticks (at 100Hz, by default)
+        * All time values are in ticks
+
+        uint16_t ao_time(void)
+
+               Returns the curent tick count
+
+       void ao_delay(uint16_t delay)
+
+               Suspend task execution for 'delay' ticks.
+
+DMA API
+
+        * Static DMA entry allocation
+
+       uint8_t ao_dma_alloc(uint8_t *done)
+
+               Allocates one of the 5 DMA units on the cc1111
+               processor. When this DMA unit is finished, it will
+               set *done to 1 and wakeup anyone waiting on that.
+
+       void ao_dma_set_transfer(uint8_t id, void *src, void *dst,
+                                uint16_t count, uint8_t cfg0, uint8_t cfg1)
+
+               Prepares the specified DMA unit for operation.
+
+       void ao_dma_start(uint8_t id)
+
+               Start DMA on the specified channel.
+
+       void ao_dma_trigger(uint8_t id)
+
+               Manually trigger a DMA channel
+
+       void ao_dma_abort(uint8_t id)
+
+               Abort a pending DMA transfer, signalling
+               that by setting the associated 'done' to
+               AO_DMA_ABORTED and waking any task
+               sleeping on that.
diff --git a/Releasing b/Releasing
new file mode 100644 (file)
index 0000000..4d2f8e7
--- /dev/null
+++ b/Releasing
@@ -0,0 +1,54 @@
+These are Bdale's notes on how to do a release.
+
+       update the version in configure.ac and commit
+
+       git tag -a <version>
+
+       - make sure checked-out tree is "clean" so git won't complain
+
+       git-buildpackage
+
+               at this point, we have Debian packages
+
+       git tag debian/<version>
+
+       make distclean
+
+       ./autogen.sh --with-fat-dir=/home/bdale/web/altusmetrum/AltOS/releases
+       make fat
+
+               this pushes packages for each platform to web site
+
+       - copy the relevant release notes file from doc/ to 
+           /home/bdale/web/altusmetrum/AltOS/releases/<rev>
+
+       - go edit ~/web/altusmetrum/AltOS/releases/<rev>.mdwn to include
+         release date and explicit ref to dir contents so the AltOS page
+         shows versioned links, commit and push
+
+       (cd doc ; make publish)
+
+               this pushes fresh documents to the web site
+
+       sudo debian/rules clean
+       git push
+       git push --tags
+
+               push commits and leave the build tree in an uncluttered state
+
+Testing before a release
+
+       To verify that a build works, the following need to be checked
+       on each platform:
+
+       1) Install package
+
+       2) Connect TM *and* TD devices. Verify that you can Monitor
+          Flight from the TD and Configure Telemetrum from the TM.
+
+       3) Replay Flight, using your favorite .eeprom file. Check
+          each tab, especially the 'Site Map' tab. Make sure the
+          sound works.
+
+       4) Graph Data. Graph a favorite .eeprom file. Make sure you
+          can zoom in on some region of the graph.
diff --git a/altosdroid/.classpath b/altosdroid/.classpath
new file mode 100644 (file)
index 0000000..0ca188f
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+       <classpathentry kind="src" path="src"/>
+       <classpathentry kind="src" path="gen"/>
+       <classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+       <classpathentry kind="output" path="bin/classes"/>
+</classpath>
diff --git a/altosdroid/.gitignore b/altosdroid/.gitignore
new file mode 100644 (file)
index 0000000..c0bb8dd
--- /dev/null
@@ -0,0 +1,3 @@
+local.properties
+bin
+gen
diff --git a/altosdroid/.project b/altosdroid/.project
new file mode 100644 (file)
index 0000000..7b56596
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>AltosDroid</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.jdt.core.javabuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+                       <arguments>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/altosdroid/.settings/org.eclipse.jdt.core.prefs b/altosdroid/.settings/org.eclipse.jdt.core.prefs
new file mode 100644 (file)
index 0000000..e5d1cd3
--- /dev/null
@@ -0,0 +1,5 @@
+#Wed Sep 28 19:51:24 NZDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/altosdroid/AndroidManifest.xml b/altosdroid/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..1239175
--- /dev/null
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.altusmetrum.AltosDroid"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:targetSdkVersion="10" android:minSdkVersion="10"/>
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+
+    <application android:label="@string/app_name"
+                 android:icon="@drawable/app_icon" >
+        <activity android:label="@string/app_name"
+                  android:configChanges="orientation|keyboardHidden" android:name="org.altusmetrum.AltosDroid.AltosDroid">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".DeviceListActivity"
+                  android:label="@string/select_device"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+
+        <service android:name=".TelemetryService" />
+
+
+    </application>
+</manifest>
diff --git a/altosdroid/Makefile.am b/altosdroid/Makefile.am
new file mode 100644 (file)
index 0000000..36d28ca
--- /dev/null
@@ -0,0 +1,54 @@
+if ANDROID
+all_target=bin/AltosDroid-debug.apk bin/AltosDroid-release.apk
+clean_command=ant clean
+else
+all_target=
+clean_command=echo done
+endif
+
+SDK=$(ANDROID_SDK)
+
+DX=$(SDK)/platform-tools/dx
+ADB=$(SDK)/platform-tools/adb
+AAPT=$(SDK)/platform-tools/aapt
+APKBUILDER=$(SDK)/tools/apkbuilder
+ZIPALIGN=$(SDK)/tools/zipalign
+
+SRC_DIR=src/org/altusmetrum/AltosDroid
+EXT_LIBDIR=libs
+ALTOSLIB_SRCDIR=../altoslib
+ALTOSLIB_JAR=AltosLib.jar
+
+ALTOSLIB=$(EXT_LIBDIR)/$(ALTOSLIB_JAR)
+
+SRC=\
+       $(SRC_DIR)/AltosDroid.java \
+       $(SRC_DIR)/TelemetryService.java \
+       $(SRC_DIR)/TelemetryReader.java \
+       $(SRC_DIR)/AltosBluetooth.java \
+       $(SRC_DIR)/DeviceListActivity.java \
+       $(SRC_DIR)/Dumper.java
+
+all: $(all_target)
+
+$(ALTOSLIB): $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR)
+       mkdir -p $(EXT_LIBDIR)
+       cd $(EXT_LIBDIR) && ln -s $(shell echo $(EXT_LIBDIR) | sed 's|[^/]\+|..|g')/$(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR) .
+
+if ANDROID
+install-release: bin/AltosDroid-release.apk
+       $(ADB) install -r bin/AltosDroid-release.apk
+
+install-debug: bin/AltosDroid-debug.apk
+       $(ADB) install -r bin/AltosDroid-debug.apk
+
+bin/AltosDroid-debug.apk: $(SRC) $(ALTOSLIB)
+       ant debug
+
+bin/AltosDroid-release.apk: $(SRC) $(ALTOSLIB)
+       ant release
+endif
+
+clean:
+       $(clean_command)
+
diff --git a/altosdroid/build.xml b/altosdroid/build.xml
new file mode 100644 (file)
index 0000000..6a89edb
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="AltosDroid" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contains the path to the SDK. It should *NOT* be checked into
+         Version Control Systems. -->
+    <property file="local.properties" />
+
+    <!-- The ant.properties file can be created by you. It is only edited by the
+         'android' tool to add properties to it.
+         This is the place to change some Ant specific build properties.
+         Here are some properties you may want to change/update:
+
+         source.dir
+             The name of the source directory. Default is 'src'.
+         out.dir
+             The name of the output directory. Default is 'bin'.
+
+         For other overridable properties, look at the beginning of the rules
+         files in the SDK, at tools/ant/build.xml
+
+         Properties related to the SDK location or the project target should
+         be updated using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems.
+
+         -->
+    <property file="ant.properties" />
+
+    <!-- The project.properties file is created and updated by the 'android'
+         tool, as well as ADT.
+
+         This contains project specific properties such as project target, and library
+         dependencies. Lower level build properties are stored in ant.properties
+         (or in .classpath for Eclipse projects).
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems. -->
+    <loadproperties srcFile="project.properties" />
+
+    <!-- quick check on sdk.dir -->
+    <fail
+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through an env var"
+            unless="sdk.dir"
+    />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+     in between standard targets -->
+<!--
+    <target name="-pre-build">
+    </target>
+    <target name="-pre-compile">
+    </target>
+
+    /* This is typically used for code obfuscation.
+       Compiled code location: ${out.classes.absolute.dir}
+       If this is not done in place, override ${out.dex.input.absolute.dir} */
+    <target name="-post-compile">
+    </target>
+-->
+
+    <!-- Import the actual build file.
+
+         To customize existing targets, there are two options:
+         - Customize only one target:
+             - copy/paste the target into this file, *before* the
+               <import> task.
+             - customize it to your needs.
+         - Customize the whole content of build.xml
+             - copy/paste the content of the rules files (minus the top node)
+               into this file, replacing the <import> task.
+             - customize to your needs.
+
+         ***********************
+         ****** IMPORTANT ******
+         ***********************
+         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+         in order to avoid having your file be overridden by tools such as "android update project"
+    -->
+    <!-- version-tag: 1 -->
+    <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
diff --git a/altosdroid/default.properties b/altosdroid/default.properties
new file mode 100644 (file)
index 0000000..66db0d1
--- /dev/null
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-10
diff --git a/altosdroid/keystore b/altosdroid/keystore
new file mode 100644 (file)
index 0000000..00739d0
Binary files /dev/null and b/altosdroid/keystore differ
diff --git a/altosdroid/libs/.gitignore b/altosdroid/libs/.gitignore
new file mode 100644 (file)
index 0000000..b4e68f6
--- /dev/null
@@ -0,0 +1 @@
+AltosLib.jar
diff --git a/altosdroid/local.properties.in b/altosdroid/local.properties.in
new file mode 100644 (file)
index 0000000..14df049
--- /dev/null
@@ -0,0 +1,10 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked in Version Control Systems,
+# as it contains information specific to your local configuration.
+
+# location of the SDK. This is only used by Ant
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=@ANDROID_SDK@
diff --git a/altosdroid/project.properties b/altosdroid/project.properties
new file mode 100644 (file)
index 0000000..0a80e64
--- /dev/null
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=Google Inc.:Google APIs:10
diff --git a/altosdroid/res/drawable-hdpi/am_status_c.png b/altosdroid/res/drawable-hdpi/am_status_c.png
new file mode 100644 (file)
index 0000000..d439321
Binary files /dev/null and b/altosdroid/res/drawable-hdpi/am_status_c.png differ
diff --git a/altosdroid/res/drawable-hdpi/am_status_g.png b/altosdroid/res/drawable-hdpi/am_status_g.png
new file mode 100644 (file)
index 0000000..03f9dd7
Binary files /dev/null and b/altosdroid/res/drawable-hdpi/am_status_g.png differ
diff --git a/altosdroid/res/drawable-hdpi/app_icon.png b/altosdroid/res/drawable-hdpi/app_icon.png
new file mode 100644 (file)
index 0000000..da2f08a
Binary files /dev/null and b/altosdroid/res/drawable-hdpi/app_icon.png differ
diff --git a/altosdroid/res/drawable-mdpi/am_status_c.png b/altosdroid/res/drawable-mdpi/am_status_c.png
new file mode 100644 (file)
index 0000000..30a8d29
Binary files /dev/null and b/altosdroid/res/drawable-mdpi/am_status_c.png differ
diff --git a/altosdroid/res/drawable-mdpi/am_status_g.png b/altosdroid/res/drawable-mdpi/am_status_g.png
new file mode 100644 (file)
index 0000000..07f7f07
Binary files /dev/null and b/altosdroid/res/drawable-mdpi/am_status_g.png differ
diff --git a/altosdroid/res/drawable/app_icon.png b/altosdroid/res/drawable/app_icon.png
new file mode 100644 (file)
index 0000000..b0a3c9c
Binary files /dev/null and b/altosdroid/res/drawable/app_icon.png differ
diff --git a/altosdroid/res/layout/altosdroid.xml b/altosdroid/res/layout/altosdroid.xml
new file mode 100644 (file)
index 0000000..f185ea9
--- /dev/null
@@ -0,0 +1,349 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="0" >
+
+        <RelativeLayout
+            android:id="@+id/strut"
+            android:layout_width="10dip"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true" >
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/callsign_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_toLeftOf="@+id/strut" >
+
+            <TextView
+                android:id="@+id/callsign_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/callsign_label" />
+
+            <TextView
+                android:id="@+id/callsign_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@id/callsign_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/rssi_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_toRightOf="@id/strut"
+            android:layout_alignParentRight="true" >
+
+            <TextView
+                android:id="@+id/rssi_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/rssi_label" />
+
+            <TextView
+                android:id="@+id/rssi_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/rssi_label"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/serial_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/callsign_container"
+            android:layout_toLeftOf="@+id/strut" >
+
+            <TextView
+                android:id="@+id/serial_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/serial_label" />
+
+            <TextView
+                android:id="@+id/serial_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/serial_label"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/flight_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/callsign_container"
+            android:layout_toRightOf="@+id/strut"
+            android:layout_alignParentRight="true" >
+
+            <TextView
+                android:id="@+id/flight_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/flight_label" />
+
+            <TextView
+                android:id="@+id/flight_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/flight_label"
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/state_container"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/serial_container" >
+
+            <TextView
+                android:id="@+id/state_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/state_label" />
+
+            <TextView
+                android:id="@+id/state_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/state_label"
+                android:layout_centerInParent="true"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:textSize="50dip" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/speed_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_below="@+id/state_container"
+            android:layout_toLeftOf="@+id/strut" >
+
+            <TextView
+                android:id="@+id/speed_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/speed_label" />
+
+            <TextView
+                android:id="@+id/speed_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@id/speed_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/accel_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_below="@+id/state_container"
+            android:layout_toRightOf="@+id/strut" >
+
+            <TextView
+                android:id="@+id/accel_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/accel_label" />
+
+            <TextView
+                android:id="@+id/accel_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/accel_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/range_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_below="@+id/speed_container"
+            android:layout_toLeftOf="@+id/strut" >
+
+            <TextView
+                android:id="@+id/range_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/range_label" />
+
+            <TextView
+                android:id="@+id/range_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/range_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/height_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_below="@id/speed_container"
+            android:layout_toRightOf="@id/strut" >
+
+            <TextView
+                android:id="@+id/height_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/height_label" />
+
+            <TextView
+                android:id="@+id/height_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/height_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/elevation_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_below="@id/range_container"
+            android:layout_toLeftOf="@id/strut" >
+
+            <TextView
+                android:id="@+id/elevation_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/elevation_label" />
+
+            <TextView
+                android:id="@+id/elevation_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/elevation_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/bearing_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_below="@+id/range_container"
+            android:layout_toRightOf="@+id/strut" >
+
+            <TextView
+                android:id="@+id/bearing_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/bearing_label" />
+
+            <TextView
+                android:id="@+id/bearing_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/bearing_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/latitude_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@+id/elevation_container" >
+
+            <TextView
+                android:id="@+id/latitude_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/latitude_label" />
+
+            <TextView
+                android:id="@+id/latitude_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/latitude_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </RelativeLayout>
+
+        <RelativeLayout
+            android:id="@+id/longitude_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/latitude_container" >
+
+            <TextView
+                android:id="@+id/longitude_label"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/longitude_label" />
+
+            <TextView
+                android:id="@+id/longitude_value"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_alignParentRight="true"
+                android:layout_below="@+id/longitude_label"
+                android:text=""
+                android:textAppearance="?android:attr/textAppearanceLarge" />
+
+        </RelativeLayout>
+
+
+        <TextView
+               android:id="@+id/text"
+               android:layout_width="fill_parent"
+               android:layout_height="0dip"
+               android:layout_alignParentBottom="true"
+               android:layout_below="@+id/longitude_container"
+               android:gravity="bottom"
+               android:scrollbars="vertical"
+               android:textSize="7dp"
+               android:typeface="monospace" />
+
+    </RelativeLayout>
diff --git a/altosdroid/res/layout/custom_title.xml b/altosdroid/res/layout/custom_title.xml
new file mode 100644 (file)
index 0000000..57eb6b4
--- /dev/null
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_vertical"
+  >
+  <TextView android:id="@+id/title_left_text"
+      android:layout_alignParentLeft="true"
+      android:ellipsize="end"
+      android:singleLine="true"
+      style="?android:attr/windowTitleStyle"
+      android:layout_width="wrap_content"
+      android:layout_height="match_parent"
+      android:layout_weight="1"
+    />
+    <TextView android:id="@+id/title_right_text"
+        android:layout_alignParentRight="true"
+        android:ellipsize="end"
+        android:singleLine="true"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:textColor="#fff"
+        android:layout_weight="1" 
+    />
+</RelativeLayout>
\ No newline at end of file
diff --git a/altosdroid/res/layout/device_list.xml b/altosdroid/res/layout/device_list.xml
new file mode 100644 (file)
index 0000000..395695f
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+    <TextView android:id="@+id/title_paired_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/title_paired_devices"
+        android:visibility="gone"
+        android:background="#666"
+        android:textColor="#fff"
+        android:paddingLeft="5dp"
+    />
+    <ListView android:id="@+id/paired_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:stackFromBottom="true"
+        android:layout_weight="1"
+    />
+    <TextView android:id="@+id/title_new_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/title_other_devices"
+        android:visibility="gone"
+        android:background="#666"
+        android:textColor="#fff"
+        android:paddingLeft="5dp"
+    />
+    <ListView android:id="@+id/new_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:stackFromBottom="true"
+        android:layout_weight="2"
+    />
+    <Button android:id="@+id/button_scan"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/button_scan"
+    />
+</LinearLayout>
\ No newline at end of file
diff --git a/altosdroid/res/layout/device_name.xml b/altosdroid/res/layout/device_name.xml
new file mode 100644 (file)
index 0000000..8fa358c
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textSize="18sp"
+    android:padding="5dp"
+/>
\ No newline at end of file
diff --git a/altosdroid/res/layout/main.xml b/altosdroid/res/layout/main.xml
new file mode 100644 (file)
index 0000000..00ca63c
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+
+    <TextView
+        android:id="@+id/in"
+        android:layout_width="fill_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:gravity="bottom"
+        android:scrollbars="vertical"
+        android:textSize="7dp"
+        android:typeface="monospace" />
+
+</LinearLayout>
diff --git a/altosdroid/res/layout/message.xml b/altosdroid/res/layout/message.xml
new file mode 100644 (file)
index 0000000..8fa358c
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textSize="18sp"
+    android:padding="5dp"
+/>
\ No newline at end of file
diff --git a/altosdroid/res/menu/option_menu.xml b/altosdroid/res/menu/option_menu.xml
new file mode 100644 (file)
index 0000000..d7ba830
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/connect_scan"
+          android:icon="@android:drawable/ic_menu_search"
+          android:title="@string/connect_device" />
+    <item android:id="@+id/select_freq"
+          android:icon="@android:drawable/ic_menu_preferences"
+          android:title="@string/select_freq" />
+</menu>
diff --git a/altosdroid/res/values/strings.xml b/altosdroid/res/values/strings.xml
new file mode 100644 (file)
index 0000000..1b28284
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="app_name">AltosDroid</string>
+
+    <!--  AltosDroid -->
+    <string name="bt_not_enabled">Bluetooth was not enabled.</string>
+    <string name="title_connecting">connecting…</string>
+    <string name="title_connected_to">connected: </string>
+    <string name="title_not_connected">not connected</string>
+
+    <!-- Options Menu -->
+    <string name="connect_device">Connect a device</string>
+    <string name="select_freq">Select radio frequency</string>
+
+    <!--  DeviceListActivity -->
+    <string name="scanning">scanning for devices…</string>
+    <string name="select_device">select a device to connect</string>
+    <string name="none_paired">No devices have been paired</string>
+    <string name="none_found">No devices found</string>
+    <string name="title_paired_devices">Paired Devices</string>
+    <string name="title_other_devices">Other Available Devices</string>
+    <string name="button_scan">Scan for devices</string>
+
+    <!-- Service -->
+    <string name="telemetry_service_label">AltosDroid Telemetry Service</string>
+    <string name="telemetry_service_started">Telemetry Service Started</string>
+    <string name="telemetry_service_stopped">Telemetry Service Stopped</string>
+
+
+       <!-- UI fields -->
+    <string name="callsign_label">Callsign</string>
+    <string name="serial_label">Serial no.</string>
+    <string name="flight_label">Flight no.</string>
+    <string name="rssi_label">RSSI</string>
+    <string name="state_label">State</string>
+    <string name="speed_label">Speed</string>
+    <string name="accel_label">Acceleration</string>
+    <string name="range_label">Range</string>
+    <string name="height_label">Height</string>
+    <string name="elevation_label">Elevation</string>
+    <string name="bearing_label">Bearing</string>
+    <string name="latitude_label">Latitude</string>
+    <string name="longitude_label">Longitude</string>
+
+</resources>
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java
new file mode 100644 (file)
index 0000000..9fcc4eb
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * 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 org.altusmetrum.AltosDroid;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.UUID;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothSocket;
+//import android.os.Bundle;
+import android.os.Handler;
+//import android.os.Message;
+import android.util.Log;
+
+import org.altusmetrum.AltosLib.*;
+
+public class AltosBluetooth extends AltosLink {
+
+       // Debugging
+       private static final String TAG = "AltosBluetooth";
+       private static final boolean D = true;
+
+       private ConnectThread    connect_thread = null;
+       private Thread           input_thread   = null;
+
+       private Handler          handler;
+
+       private BluetoothAdapter adapter;
+       private BluetoothDevice  device;
+       private BluetoothSocket  socket;
+       private InputStream      input;
+       private OutputStream     output;
+
+       // Constructor
+       public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) {
+               adapter = BluetoothAdapter.getDefaultAdapter();
+               device = in_device;
+               handler = in_handler;
+
+               connect_thread = new ConnectThread(device);
+               connect_thread.start();
+
+       }
+
+       private class ConnectThread extends Thread {
+               private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
+
+               public ConnectThread(BluetoothDevice device) {
+                       BluetoothSocket tmp_socket = null;
+
+                       try {
+                               tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
+                       } catch (IOException e) {
+                               e.printStackTrace();
+                       }
+                       socket = tmp_socket;
+               }
+
+               public void run() {
+                       if (D) Log.d(TAG, "ConnectThread: BEGIN");
+                       setName("ConnectThread");
+
+                       // Always cancel discovery because it will slow down a connection
+                       adapter.cancelDiscovery();
+
+                       synchronized (AltosBluetooth.this) {
+                               // Make a connection to the BluetoothSocket
+                               try {
+                                       // This is a blocking call and will only return on a
+                                       // successful connection or an exception
+                                       socket.connect();
+
+                                       input = socket.getInputStream();
+                                       output = socket.getOutputStream();
+                               } catch (IOException e) {
+                                       // Close the socket
+                                       try {
+                                               socket.close();
+                                       } catch (IOException e2) {
+                                               if (D) Log.e(TAG, "ConnectThread: Failed to close() socket after failed connection");
+                                       }
+                                       input = null;
+                                       output = null;
+                                       AltosBluetooth.this.notifyAll();
+                                       handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED).sendToTarget();
+                                       if (D) Log.e(TAG, "ConnectThread: Failed to establish connection");
+                                       return;
+                               }
+
+                               input_thread = new Thread(AltosBluetooth.this);
+                               input_thread.start();
+
+                               // Configure the newly connected device for telemetry
+                               print("~\nE 0\n");
+                               set_monitor(false);
+
+                               // Let TelemetryService know we're connected
+                               handler.obtainMessage(TelemetryService.MSG_CONNECTED).sendToTarget();
+
+                               // Notify other waiting threads, now that we're connected
+                               AltosBluetooth.this.notifyAll();
+
+                               // Reset the ConnectThread because we're done
+                               connect_thread = null;
+
+                               if (D) Log.d(TAG, "ConnectThread: Connect completed");
+                       }
+               }
+
+               public void cancel() {
+                       try {
+                               if (socket != null)
+                                       socket.close();
+                       } catch (IOException e) {
+                               if (D) Log.e(TAG, "ConnectThread: close() of connect socket failed", e);
+                       }
+               }
+       }
+
+       private synchronized void wait_connected() throws InterruptedException, IOException {
+               if (input == null) {
+                       wait();
+                       if (input == null) throw new IOException();
+               }
+       }
+
+       private void connection_lost() {
+               if (D) Log.e(TAG, "Connection lost during I/O");
+               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED).sendToTarget();
+       }
+
+       public void print(String data) {
+               byte[] bytes = data.getBytes();
+               if (D) Log.d(TAG, "print(): begin");
+               try {
+                       wait_connected();
+                       output.write(bytes);
+                       if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
+               } catch (IOException e) {
+                       connection_lost();
+               } catch (InterruptedException e) {
+                       connection_lost();
+               }
+       }
+
+       public int getchar() {
+               try {
+                       wait_connected();
+                       return input.read();
+               } catch (IOException e) {
+                       connection_lost();
+               } catch (java.lang.InterruptedException e) {
+                       connection_lost();
+               }
+               return AltosLink.ERROR;
+       }
+
+       public void close() {
+               if (D) Log.d(TAG, "close(): begin");
+               synchronized(this) {
+                       if (D) Log.d(TAG, "close(): synched");
+
+                       if (connect_thread != null) {
+                               if (D) Log.d(TAG, "close(): stopping connect_thread");
+                               connect_thread.cancel();
+                               connect_thread = null;
+                       }
+                       if (D) Log.d(TAG, "close(): Closing socket");
+                       try {
+                               socket.close();
+                       } catch (IOException e) {
+                               if (D) Log.e(TAG, "close(): unable to close() socket");
+                       }
+                       if (input_thread != null) {
+                               if (D) Log.d(TAG, "close(): stopping input_thread");
+                               try {
+                                       if (D) Log.d(TAG, "close(): input_thread.interrupt().....");
+                                       input_thread.interrupt();
+                                       if (D) Log.d(TAG, "close(): input_thread.join().....");
+                                       input_thread.join();
+                               } catch (Exception e) {}
+                               input_thread = null;
+                       }
+                       input = null;
+                       output = null;
+                       notifyAll();
+               }
+       }
+
+
+       // We override this method so that we can add some debugging. Not 100% elegant, but more useful
+       // than debugging one char at a time above in getchar()!
+       public void add_reply(AltosLine line) throws InterruptedException {
+               if (D) Log.d(TAG, String.format("Got REPLY: %s", line.line));
+               super.add_reply(line);
+       }
+
+       //public void flush_output() { super.flush_output(); }
+
+       // Stubs of required methods when extending AltosLink
+       public boolean can_cancel_reply()   { return false; }
+       public boolean show_reply_timeout() { return true; }
+       public void hide_reply_timeout()    { }
+
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java
new file mode 100644 (file)
index 0000000..0068968
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * 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 org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.Intent;
+import android.content.Context;
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+import android.content.DialogInterface;
+import android.os.IBinder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.Window;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.app.AlertDialog;
+
+import org.altusmetrum.AltosLib.*;
+
+/**
+ * This is the main Activity that displays the current chat session.
+ */
+public class AltosDroid extends Activity {
+       // Debugging
+       private static final String TAG = "AltosDroid";
+       private static final boolean D = true;
+
+       // Message types received by our Handler
+       public static final int MSG_STATE_CHANGE    = 1;
+       public static final int MSG_TELEMETRY       = 2;
+
+       // Intent request codes
+       private static final int REQUEST_CONNECT_DEVICE = 1;
+       private static final int REQUEST_ENABLE_BT      = 2;
+
+       // Layout Views
+       private TextView mTitle;
+
+       // Flight state values
+       private TextView mCallsignView;
+       private TextView mRSSIView;
+       private TextView mSerialView;
+       private TextView mFlightView;
+       private TextView mStateView;
+       private TextView mSpeedView;
+       private TextView mAccelView;
+       private TextView mRangeView;
+       private TextView mHeightView;
+       private TextView mElevationView;
+       private TextView mBearingView;
+       private TextView mLatitudeView;
+       private TextView mLongitudeView;
+
+       // Generic field for extras at the bottom
+       private TextView mTextView;
+
+       // Service
+       private boolean mIsBound   = false;
+       private Messenger mService = null;
+       final Messenger mMessenger = new Messenger(new IncomingHandler(this));
+
+       // TeleBT Config data
+       private AltosConfigData mConfigData = null;
+       // Local Bluetooth adapter
+       private BluetoothAdapter mBluetoothAdapter = null;
+
+       // Text to Speech
+       private AltosVoice mAltosVoice = null;
+
+       // The Handler that gets information back from the Telemetry Service
+       static class IncomingHandler extends Handler {
+               private final WeakReference<AltosDroid> mAltosDroid;
+               IncomingHandler(AltosDroid ad) { mAltosDroid = new WeakReference<AltosDroid>(ad); }
+
+               @Override
+               public void handleMessage(Message msg) {
+                       AltosDroid ad = mAltosDroid.get();
+                       switch (msg.what) {
+                       case MSG_STATE_CHANGE:
+                               if(D) Log.d(TAG, "MSG_STATE_CHANGE: " + msg.arg1);
+                               switch (msg.arg1) {
+                               case TelemetryService.STATE_CONNECTED:
+                                       ad.mConfigData = (AltosConfigData) msg.obj;
+                                       String str = String.format(" %s S/N: %d", ad.mConfigData.product, ad.mConfigData.serial);
+                                       ad.mTitle.setText(R.string.title_connected_to);
+                                       ad.mTitle.append(str);
+                                       Toast.makeText(ad.getApplicationContext(), "Connected to " + str, Toast.LENGTH_SHORT).show();
+                                       ad.mAltosVoice.speak("Connected");
+                                       //TEST!
+                                       ad.mTextView.setText(Dumper.dump(ad.mConfigData));
+                                       break;
+                               case TelemetryService.STATE_CONNECTING:
+                                       ad.mTitle.setText(R.string.title_connecting);
+                                       break;
+                               case TelemetryService.STATE_READY:
+                               case TelemetryService.STATE_NONE:
+                                       ad.mConfigData = null;
+                                       ad.mTitle.setText(R.string.title_not_connected);
+                                       ad.mTextView.setText("");
+                                       break;
+                               }
+                               break;
+                       case MSG_TELEMETRY:
+                               ad.update_ui((AltosState) msg.obj);
+                               // TEST!
+                               ad.mTextView.setText(Dumper.dump(msg.obj));
+                               break;
+                       }
+               }
+       };
+
+
+       private ServiceConnection mConnection = new ServiceConnection() {
+               public void onServiceConnected(ComponentName className, IBinder service) {
+                       mService = new Messenger(service);
+                       try {
+                               Message msg = Message.obtain(null, TelemetryService.MSG_REGISTER_CLIENT);
+                               msg.replyTo = mMessenger;
+                               mService.send(msg);
+                       } catch (RemoteException e) {
+                               // In this case the service has crashed before we could even do anything with it
+                       }
+               }
+
+               public void onServiceDisconnected(ComponentName className) {
+                       // This is called when the connection with the service has been unexpectedly disconnected - process crashed.
+                       mService = null;
+               }
+       };
+
+
+       void doBindService() {
+               bindService(new Intent(this, TelemetryService.class), mConnection, Context.BIND_AUTO_CREATE);
+               mIsBound = true;
+       }
+
+       void doUnbindService() {
+               if (mIsBound) {
+                       // If we have received the service, and hence registered with it, then now is the time to unregister.
+                       if (mService != null) {
+                               try {
+                                       Message msg = Message.obtain(null, TelemetryService.MSG_UNREGISTER_CLIENT);
+                                       msg.replyTo = mMessenger;
+                                       mService.send(msg);
+                               } catch (RemoteException e) {
+                                       // There is nothing special we need to do if the service has crashed.
+                               }
+                       }
+                       // Detach our existing connection.
+                       unbindService(mConnection);
+                       mIsBound = false;
+               }
+       }
+
+       void update_ui(AltosState state) {
+               mCallsignView.setText(state.data.callsign);
+               mRSSIView.setText(String.format("%d", state.data.rssi));
+               mSerialView.setText(String.format("%d", state.data.serial));
+               mFlightView.setText(String.format("%d", state.data.flight));
+               mStateView.setText(state.data.state());
+               double speed = state.speed;
+               if (!state.ascent)
+                       speed = state.baro_speed;
+               mSpeedView.setText(String.format("%6.0f m/s", speed));
+               mAccelView.setText(String.format("%6.0f m/s²", state.acceleration));
+               mRangeView.setText(String.format("%6.0f m", state.range));
+               mHeightView.setText(String.format("%6.0f m", state.height));
+               mElevationView.setText(String.format("%3.0f°", state.elevation));
+               if (state.from_pad != null)
+                       mBearingView.setText(String.format("%3.0f°", state.from_pad.bearing));
+               mLatitudeView.setText(pos(state.gps.lat, "N", "S"));
+               mLongitudeView.setText(pos(state.gps.lon, "W", "E"));
+
+               mAltosVoice.tell(state);
+       }
+
+       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("%d° %9.6f\" %s", deg, min, h);
+       }
+
+       @Override
+       public void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+               if(D) Log.e(TAG, "+++ ON CREATE +++");
+
+               // Get local Bluetooth adapter
+               mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+
+               // If the adapter is null, then Bluetooth is not supported
+               if (mBluetoothAdapter == null) {
+                       Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
+                       finish();
+                       return;
+               }
+
+               // Set up the window layout
+               requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
+               //setContentView(R.layout.main);
+               setContentView(R.layout.altosdroid);
+               getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
+
+               // Set up the custom title
+               mTitle = (TextView) findViewById(R.id.title_left_text);
+               mTitle.setText(R.string.app_name);
+               mTitle = (TextView) findViewById(R.id.title_right_text);
+
+               // Set up the temporary Text View
+               mTextView = (TextView) findViewById(R.id.text);
+               mTextView.setMovementMethod(new ScrollingMovementMethod());
+               mTextView.setClickable(false);
+               mTextView.setLongClickable(false);
+
+               mCallsignView  = (TextView) findViewById(R.id.callsign_value);
+               mRSSIView      = (TextView) findViewById(R.id.rssi_value);
+               mSerialView    = (TextView) findViewById(R.id.serial_value);
+               mFlightView    = (TextView) findViewById(R.id.flight_value);
+               mStateView     = (TextView) findViewById(R.id.state_value);
+               mSpeedView     = (TextView) findViewById(R.id.speed_value);
+               mAccelView     = (TextView) findViewById(R.id.accel_value);
+               mRangeView     = (TextView) findViewById(R.id.range_value);
+               mHeightView    = (TextView) findViewById(R.id.height_value);
+               mElevationView = (TextView) findViewById(R.id.elevation_value);
+               mBearingView   = (TextView) findViewById(R.id.bearing_value);
+               mLatitudeView  = (TextView) findViewById(R.id.latitude_value);
+               mLongitudeView = (TextView) findViewById(R.id.longitude_value);
+
+               mAltosVoice = new AltosVoice(this);
+       }
+
+       @Override
+       public void onStart() {
+               super.onStart();
+               if(D) Log.e(TAG, "++ ON START ++");
+
+               if (!mBluetoothAdapter.isEnabled()) {
+                       Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+                       startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
+               }
+
+               // Start Telemetry Service
+               startService(new Intent(AltosDroid.this, TelemetryService.class));
+
+               doBindService();
+       }
+
+       @Override
+       public synchronized void onResume() {
+               super.onResume();
+               if(D) Log.e(TAG, "+ ON RESUME +");
+       }
+
+       @Override
+       public synchronized void onPause() {
+               super.onPause();
+               if(D) Log.e(TAG, "- ON PAUSE -");
+       }
+
+       @Override
+       public void onStop() {
+               super.onStop();
+               if(D) Log.e(TAG, "-- ON STOP --");
+
+               doUnbindService();
+       }
+
+       @Override
+       public void onDestroy() {
+               super.onDestroy();
+               if(D) Log.e(TAG, "--- ON DESTROY ---");
+
+               mAltosVoice.stop();
+       }
+
+
+
+
+       public void onActivityResult(int requestCode, int resultCode, Intent data) {
+               if(D) Log.d(TAG, "onActivityResult " + resultCode);
+               switch (requestCode) {
+               case REQUEST_CONNECT_DEVICE:
+                       // When DeviceListActivity returns with a device to connect to
+                       if (resultCode == Activity.RESULT_OK) {
+                               connectDevice(data);
+                       }
+                       break;
+               case REQUEST_ENABLE_BT:
+                       // When the request to enable Bluetooth returns
+                       if (resultCode == Activity.RESULT_OK) {
+                               // Bluetooth is now enabled, so set up a chat session
+                               //setupChat();
+                       } else {
+                               // User did not enable Bluetooth or an error occured
+                               Log.e(TAG, "BT not enabled");
+                               stopService(new Intent(AltosDroid.this, TelemetryService.class));
+                               Toast.makeText(this, R.string.bt_not_enabled, Toast.LENGTH_SHORT).show();
+                               finish();
+                       }
+                       break;
+               }
+       }
+
+       private void connectDevice(Intent data) {
+               // Get the device MAC address
+               String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
+               // Get the BLuetoothDevice object
+               BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+               // Attempt to connect to the device
+               try {
+                       if (D) Log.d(TAG, "Connecting to " + device.getName());
+                       mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, device));
+               } catch (RemoteException e) {
+               }
+       }
+
+       @Override
+       public boolean onCreateOptionsMenu(Menu menu) {
+               MenuInflater inflater = getMenuInflater();
+               inflater.inflate(R.menu.option_menu, menu);
+               return true;
+       }
+
+       void setFrequency(double freq) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq));
+               } catch (RemoteException e) {
+               }
+       }
+
+       void setFrequency(String freq) {
+               try {
+                       setFrequency (Double.parseDouble(freq.substring(11, 17)));
+               } catch (NumberFormatException e) {
+               }
+       }
+
+       @Override
+       public boolean onOptionsItemSelected(MenuItem item) {
+               Intent serverIntent = null;
+               switch (item.getItemId()) {
+               case R.id.connect_scan:
+                       // Launch the DeviceListActivity to see devices and do scan
+                       serverIntent = new Intent(this, DeviceListActivity.class);
+                       startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
+                       return true;
+               case R.id.select_freq:
+                       // Set the TBT radio frequency
+
+                       final String[] frequencies = {
+                               "Channel 0 (434.550MHz)",
+                               "Channel 1 (434.650MHz)",
+                               "Channel 2 (434.750MHz)",
+                               "Channel 3 (434.850MHz)",
+                               "Channel 4 (434.950MHz)",
+                               "Channel 5 (435.050MHz)",
+                               "Channel 6 (435.150MHz)",
+                               "Channel 7 (435.250MHz)",
+                               "Channel 8 (435.350MHz)",
+                               "Channel 9 (435.450MHz)"
+                       };
+
+                       AlertDialog.Builder builder = new AlertDialog.Builder(this);
+                       builder.setTitle("Pick a frequency");
+                       builder.setItems(frequencies,
+                                        new DialogInterface.OnClickListener() {
+                                                public void onClick(DialogInterface dialog, int item) {
+                                                        setFrequency(frequencies[item]);
+                                                }
+                                        });
+                       AlertDialog alert = builder.create();
+                       alert.show();
+                       return true;
+               }
+               return false;
+       }
+
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java
new file mode 100644 (file)
index 0000000..3382d55
--- /dev/null
@@ -0,0 +1,203 @@
+/*\r
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>\r
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; version 2 of the License.\r
+ *\r
+ * This program is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.\r
+ */\r
+\r
+package org.altusmetrum.AltosDroid;\r
+\r
+import android.speech.tts.TextToSpeech;\r
+import android.speech.tts.TextToSpeech.OnInitListener;\r
+\r
+import org.altusmetrum.AltosLib.*;\r
+\r
+public class AltosVoice {\r
+\r
+       private TextToSpeech tts         = null;\r
+       private boolean      tts_enabled = false;\r
+\r
+       private IdleThread   idle_thread = null;\r
+\r
+       private AltosState   old_state   = null;\r
+\r
+       public AltosVoice(AltosDroid a) {\r
+\r
+               tts = new TextToSpeech(a, new OnInitListener() {\r
+                       public void onInit(int status) {\r
+                               if (status == TextToSpeech.SUCCESS) tts_enabled = true;\r
+                               if (tts_enabled) {\r
+                                       speak("AltosDroid ready");\r
+                                       idle_thread = new IdleThread();\r
+                               }\r
+                       }\r
+               });\r
+\r
+       }\r
+\r
+       public void speak(String s) {\r
+               if (!tts_enabled) return;\r
+               tts.speak(s, TextToSpeech.QUEUE_ADD, null);\r
+       }\r
+\r
+       public void stop() {\r
+               if (tts != null) tts.shutdown();\r
+               if (idle_thread != null) {\r
+                       idle_thread.interrupt();\r
+                       idle_thread = null;\r
+               }\r
+       }\r
+\r
+       public void tell(AltosState state) {\r
+               if (!tts_enabled) return;\r
+\r
+               boolean spoke = false;\r
+               if (old_state == null || old_state.state != state.state) {\r
+                       speak(state.data.state());\r
+                       if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&\r
+                           state.state > AltosLib.ao_flight_boost) {\r
+                               speak(String.format("max speed: %d meters per second.", (int) (state.max_speed + 0.5)));\r
+                               spoke = true;\r
+                       } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&\r
+                                  state.state >= AltosLib.ao_flight_drogue) {\r
+                               speak(String.format("max height: %d meters.", (int) (state.max_height + 0.5)));\r
+                               spoke = true;\r
+                       }\r
+               }\r
+               if (old_state == null || old_state.gps_ready != state.gps_ready) {\r
+                       if (state.gps_ready) {\r
+                               speak("GPS ready");\r
+                               spoke = true;\r
+                       } else if (old_state != null) {\r
+                               speak("GPS lost");\r
+                               spoke = true;\r
+                       }\r
+               }\r
+               old_state = state;\r
+               idle_thread.notice(state, spoke);\r
+       }\r
+\r
+\r
+       class IdleThread extends Thread {\r
+               boolean            started;\r
+               private AltosState state;\r
+               int                reported_landing;\r
+               int                report_interval;\r
+               long               report_time;\r
+\r
+               public synchronized void report(boolean last) {\r
+                       if (state == null)\r
+                               return;\r
+\r
+                       /* reset the landing count once we hear about a new flight */\r
+                       if (state.state < AltosLib.ao_flight_drogue)\r
+                               reported_landing = 0;\r
+\r
+                       /* Shut up once the rocket is on the ground */\r
+                       if (reported_landing > 2) {\r
+                               return;\r
+                       }\r
+\r
+                       /* If the rocket isn't on the pad, then report height */\r
+                       if (AltosLib.ao_flight_drogue <= state.state &&\r
+                           state.state < AltosLib.ao_flight_landed &&\r
+                           state.range >= 0)\r
+                       {\r
+                               speak(String.format("Height %d, bearing %s %d, elevation %d, range %d.\n",\r
+                                                   (int) (state.height + 0.5),\r
+                                       state.from_pad.bearing_words(\r
+                                             AltosGreatCircle.BEARING_VOICE),\r
+                                                   (int) (state.from_pad.bearing + 0.5),\r
+                                                   (int) (state.elevation + 0.5),\r
+                                                   (int) (state.range + 0.5)));\r
+                       } else if (state.state > AltosLib.ao_flight_pad) {\r
+                               speak(String.format("%d meters", (int) (state.height + 0.5)));\r
+                       } else {\r
+                               reported_landing = 0;\r
+                       }\r
+\r
+                       /* If the rocket is coming down, check to see if it has landed;\r
+                        * either we've got a landed report or we haven't heard from it in\r
+                        * a long time\r
+                        */\r
+                       if (state.state >= AltosLib.ao_flight_drogue &&\r
+                           (last ||\r
+                            System.currentTimeMillis() - state.report_time >= 15000 ||\r
+                            state.state == AltosLib.ao_flight_landed))\r
+                       {\r
+                               if (Math.abs(state.baro_speed) < 20 && state.height < 100)\r
+                                       speak("rocket landed safely");\r
+                               else\r
+                                       speak("rocket may have crashed");\r
+                               if (state.from_pad != null)\r
+                                       speak(String.format("Bearing %d degrees, range %d meters.",\r
+                                                           (int) (state.from_pad.bearing + 0.5),\r
+                                                           (int) (state.from_pad.distance + 0.5)));\r
+                               ++reported_landing;\r
+                       }\r
+               }\r
+\r
+               long now () {\r
+                       return System.currentTimeMillis();\r
+               }\r
+\r
+               void set_report_time() {\r
+                       report_time = now() + report_interval;\r
+               }\r
+\r
+               public void run () {\r
+                       try {\r
+                               for (;;) {\r
+                                       set_report_time();\r
+                                       for (;;) {\r
+                                               synchronized (this) {\r
+                                                       long sleep_time = report_time - now();\r
+                                                       if (sleep_time <= 0)\r
+                                                               break;\r
+                                                       wait(sleep_time);\r
+                                               }\r
+                                       }\r
+                                       report(false);\r
+                               }\r
+                       } catch (InterruptedException ie) {\r
+                       }\r
+               }\r
+\r
+               public synchronized void notice(AltosState new_state, boolean spoken) {\r
+                       AltosState old_state = state;\r
+                       state = new_state;\r
+                       if (!started && state.state > AltosLib.ao_flight_pad) {\r
+                               started = true;\r
+                               start();\r
+                       }\r
+\r
+                       if (state.state < AltosLib.ao_flight_drogue)\r
+                               report_interval = 10000;\r
+                       else\r
+                               report_interval = 20000;\r
+                       if (old_state != null && old_state.state != state.state) {\r
+                               report_time = now();\r
+                               this.notify();\r
+                       } else if (spoken)\r
+                               set_report_time();\r
+               }\r
+\r
+               public IdleThread() {\r
+                       state = null;\r
+                       reported_landing = 0;\r
+                       report_interval = 10000;\r
+               }\r
+       }\r
+\r
+}\r
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java
new file mode 100644 (file)
index 0000000..7b9cbde
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.altusmetrum.AltosDroid;
+
+import java.util.Set;
+import org.altusmetrum.AltosDroid.R;
+
+import android.app.Activity;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
+
+/**
+ * This Activity appears as a dialog. It lists any paired devices and
+ * devices detected in the area after discovery. When a device is chosen
+ * by the user, the MAC address of the device is sent back to the parent
+ * Activity in the result Intent.
+ */
+public class DeviceListActivity extends Activity {
+       // Debugging
+       private static final String TAG = "DeviceListActivity";
+       private static final boolean D = true;
+
+       // Return Intent extra
+       public static String EXTRA_DEVICE_ADDRESS = "device_address";
+
+       // Member fields
+       private BluetoothAdapter mBtAdapter;
+       private ArrayAdapter<String> mPairedDevicesArrayAdapter;
+       private ArrayAdapter<String> mNewDevicesArrayAdapter;
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.device_list);
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+
+               // Initialize the button to perform device discovery
+               Button scanButton = (Button) findViewById(R.id.button_scan);
+               scanButton.setOnClickListener(new OnClickListener() {
+                       public void onClick(View v) {
+                               doDiscovery();
+                               v.setVisibility(View.GONE);
+                       }
+               });
+
+               // Initialize array adapters. One for already paired devices and
+               // one for newly discovered devices
+               mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
+               mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
+
+               // Find and set up the ListView for paired devices
+               ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
+               pairedListView.setAdapter(mPairedDevicesArrayAdapter);
+               pairedListView.setOnItemClickListener(mDeviceClickListener);
+
+               // Find and set up the ListView for newly discovered devices
+               ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
+               newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
+               newDevicesListView.setOnItemClickListener(mDeviceClickListener);
+
+               // Register for broadcasts when a device is discovered
+               IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
+               this.registerReceiver(mReceiver, filter);
+
+               // Register for broadcasts when discovery has finished
+               filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
+               this.registerReceiver(mReceiver, filter);
+
+               // Get the local Bluetooth adapter
+               mBtAdapter = BluetoothAdapter.getDefaultAdapter();
+
+               // Get a set of currently paired devices
+               Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
+
+               // If there are paired devices, add each one to the ArrayAdapter
+               if (pairedDevices.size() > 0) {
+                       findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
+                       for (BluetoothDevice device : pairedDevices) {
+                               mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+                       }
+               } else {
+                       String noDevices = getResources().getText(R.string.none_paired).toString();
+                       mPairedDevicesArrayAdapter.add(noDevices);
+               }
+       }
+
+       @Override
+       protected void onDestroy() {
+               super.onDestroy();
+
+               // Make sure we're not doing discovery anymore
+               if (mBtAdapter != null) {
+                       mBtAdapter.cancelDiscovery();
+               }
+
+               // Unregister broadcast listeners
+               this.unregisterReceiver(mReceiver);
+       }
+
+       /**
+       * Start device discover with the BluetoothAdapter
+       */
+       private void doDiscovery() {
+               if (D) Log.d(TAG, "doDiscovery()");
+
+               // Indicate scanning in the title
+               setProgressBarIndeterminateVisibility(true);
+               setTitle(R.string.scanning);
+
+               // Turn on sub-title for new devices
+               findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
+
+               // If we're already discovering, stop it
+               if (mBtAdapter.isDiscovering()) {
+                       mBtAdapter.cancelDiscovery();
+               }
+
+               // Request discover from BluetoothAdapter
+               mBtAdapter.startDiscovery();
+       }
+
+       // The on-click listener for all devices in the ListViews
+       private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
+               public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
+                       // Cancel discovery because it's costly and we're about to connect
+                       mBtAdapter.cancelDiscovery();
+
+                       // Get the device MAC address, which is the last 17 chars in the View
+                       String info = ((TextView) v).getText().toString();
+                       String address = info.substring(info.length() - 17);
+
+                       // Create the result Intent and include the MAC address
+                       Intent intent = new Intent();
+                       intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
+
+                       // Set result and finish this Activity
+                       setResult(Activity.RESULT_OK, intent);
+                       finish();
+               }
+       };
+
+       // The BroadcastReceiver that listens for discovered devices and
+       // changes the title when discovery is finished
+       private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+               @Override
+               public void onReceive(Context context, Intent intent) {
+                       String action = intent.getAction();
+
+                       // When discovery finds a device
+                       if (BluetoothDevice.ACTION_FOUND.equals(action)) {
+                               // Get the BluetoothDevice object from the Intent
+                               BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                               // If it's already paired, skip it, because it's been listed already
+                               if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
+                                       mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+                               }
+                       // When discovery is finished, change the Activity title
+                       } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
+                               setProgressBarIndeterminateVisibility(false);
+                               setTitle(R.string.select_device);
+                               if (mNewDevicesArrayAdapter.getCount() == 0) {
+                                       String noDevices = getResources().getText(R.string.none_found).toString();
+                                       mNewDevicesArrayAdapter.add(noDevices);
+                               }
+                       }
+               }
+       };
+
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/Dumper.java b/altosdroid/src/org/altusmetrum/AltosDroid/Dumper.java
new file mode 100644 (file)
index 0000000..17e4cf5
--- /dev/null
@@ -0,0 +1,183 @@
+package org.altusmetrum.AltosDroid;\r
+\r
+       import java.lang.reflect.Array;\r
+       import java.lang.reflect.Field;\r
+       import java.util.HashMap;\r
+\r
+       public class Dumper {\r
+               private static Dumper instance = new Dumper();\r
+\r
+               protected static Dumper getInstance() {\r
+                       return instance;\r
+               }\r
+\r
+               class DumpContext {\r
+                       int maxDepth = 0;\r
+                       int maxArrayElements = 0;\r
+                       int callCount = 0;\r
+                       HashMap<String, String> ignoreList = new HashMap<String, String>();\r
+                       HashMap<Object, Integer> visited = new HashMap<Object, Integer>();\r
+               }\r
+\r
+               public static String dump(Object o) {\r
+                       return dump(o, 0, 0, null);\r
+               }\r
+\r
+               public static String dump(Object o, int maxDepth, int maxArrayElements, String[] ignoreList) {\r
+                       DumpContext ctx = Dumper.getInstance().new DumpContext();\r
+                       ctx.maxDepth = maxDepth;\r
+                       ctx.maxArrayElements = maxArrayElements;\r
+\r
+                       if (ignoreList != null) {\r
+                               for (int i = 0; i < Array.getLength(ignoreList); i++) {\r
+                                       int colonIdx = ignoreList[i].indexOf(':');\r
+                                       if (colonIdx == -1)\r
+                                               ignoreList[i] = ignoreList[i] + ":";\r
+                                       ctx.ignoreList.put(ignoreList[i], ignoreList[i]);\r
+                               }\r
+                       }\r
+\r
+                       return dump(o, ctx);\r
+               }\r
+\r
+               protected static String dump(Object o, DumpContext ctx) {\r
+                       if (o == null) {\r
+                               return "<null>";\r
+                       }\r
+\r
+                       ctx.callCount++;\r
+                       StringBuffer tabs = new StringBuffer();\r
+                       for (int k = 0; k < ctx.callCount; k++) {\r
+                               tabs.append("\t");\r
+                       }\r
+                       StringBuffer buffer = new StringBuffer();\r
+                       @SuppressWarnings("rawtypes")\r
+                       Class oClass = o.getClass();\r
+\r
+                       String oSimpleName = getSimpleNameWithoutArrayQualifier(oClass);\r
+\r
+                       if (ctx.ignoreList.get(oSimpleName + ":") != null)\r
+                               return "<Ignored>";\r
+\r
+                       if (oClass.isArray()) {\r
+                               buffer.append("\n");\r
+                               buffer.append(tabs.toString().substring(1));\r
+                               buffer.append("[\n");\r
+                               int rowCount = ctx.maxArrayElements == 0 ? Array.getLength(o) : Math.min(ctx.maxArrayElements, Array.getLength(o));\r
+                               for (int i = 0; i < rowCount; i++) {\r
+                                       buffer.append(tabs.toString());\r
+                                       try {\r
+                                               Object value = Array.get(o, i);\r
+                                               buffer.append(dumpValue(value, ctx));\r
+                                       } catch (Exception e) {\r
+                                               buffer.append(e.getMessage());\r
+                                       }\r
+                                       if (i < Array.getLength(o) - 1)\r
+                                               buffer.append(",");\r
+                                       buffer.append("\n");\r
+                               }\r
+                               if (rowCount < Array.getLength(o)) {\r
+                                       buffer.append(tabs.toString());\r
+                                       buffer.append(Array.getLength(o) - rowCount + " more array elements...");\r
+                                       buffer.append("\n");\r
+                               }\r
+                               buffer.append(tabs.toString().substring(1));\r
+                               buffer.append("]");\r
+                       } else {\r
+                               buffer.append("\n");\r
+                               buffer.append(tabs.toString().substring(1));\r
+                               buffer.append("{\n");\r
+                               buffer.append(tabs.toString());\r
+                               buffer.append("hashCode: " + o.hashCode());\r
+                               buffer.append("\n");\r
+                               while (oClass != null && oClass != Object.class) {\r
+                                       Field[] fields = oClass.getDeclaredFields();\r
+\r
+                                       if (ctx.ignoreList.get(oClass.getSimpleName()) == null) {\r
+                                               if (oClass != o.getClass()) {\r
+                                                       buffer.append(tabs.toString().substring(1));\r
+                                                       buffer.append("  Inherited from superclass " + oSimpleName + ":\n");\r
+                                               }\r
+\r
+                                               for (int i = 0; i < fields.length; i++) {\r
+\r
+                                                       String fSimpleName = getSimpleNameWithoutArrayQualifier(fields[i].getType());\r
+                                                       String fName = fields[i].getName();\r
+\r
+                                                       fields[i].setAccessible(true);\r
+                                                       buffer.append(tabs.toString());\r
+                                                       buffer.append(fName + "(" + fSimpleName + ")");\r
+                                                       buffer.append("=");\r
+\r
+                                                       if (ctx.ignoreList.get(":" + fName) == null &&\r
+                                                               ctx.ignoreList.get(fSimpleName + ":" + fName) == null &&\r
+                                                               ctx.ignoreList.get(fSimpleName + ":") == null) {\r
+\r
+                                                               try {\r
+                                                                       Object value = fields[i].get(o);\r
+                                                                       buffer.append(dumpValue(value, ctx));\r
+                                                               } catch (Exception e) {\r
+                                                                       buffer.append(e.getMessage());\r
+                                                               }\r
+                                                               buffer.append("\n");\r
+                                                       } else {\r
+                                                               buffer.append("<Ignored>");\r
+                                                               buffer.append("\n");\r
+                                                       }\r
+                                               }\r
+                                               oClass = oClass.getSuperclass();\r
+                                               oSimpleName = oClass.getSimpleName();\r
+                                       } else {\r
+                                               oClass = null;\r
+                                               oSimpleName = "";\r
+                                       }\r
+                               }\r
+                               buffer.append(tabs.toString().substring(1));\r
+                               buffer.append("}");\r
+                       }\r
+                       ctx.callCount--;\r
+                       return buffer.toString();\r
+               }\r
+\r
+               protected static String dumpValue(Object value, DumpContext ctx) {\r
+                       if (value == null) {\r
+                               return "<null>";\r
+                       }\r
+                       if (value.getClass().isPrimitive() ||\r
+                               value.getClass() == java.lang.Short.class ||\r
+                               value.getClass() == java.lang.Long.class ||\r
+                               value.getClass() == java.lang.String.class ||\r
+                               value.getClass() == java.lang.Integer.class ||\r
+                               value.getClass() == java.lang.Float.class ||\r
+                               value.getClass() == java.lang.Byte.class ||\r
+                               value.getClass() == java.lang.Character.class ||\r
+                               value.getClass() == java.lang.Double.class ||\r
+                               value.getClass() == java.lang.Boolean.class) {\r
+\r
+                               return value.toString();\r
+\r
+                       } else {\r
+\r
+                               Integer visitedIndex = ctx.visited.get(value);\r
+                               if (visitedIndex == null) {\r
+                                       ctx.visited.put(value, ctx.callCount);\r
+                                       if (ctx.maxDepth == 0 || ctx.callCount < ctx.maxDepth) {\r
+                                               return dump(value, ctx);\r
+                                       } else {\r
+                                               return "<Reached max recursion depth>";\r
+                                       }\r
+                               } else {\r
+                                       return "<Previously visited - see hashCode " + value.hashCode() + ">";\r
+                               }\r
+                       }\r
+               }\r
+\r
+\r
+               private static String getSimpleNameWithoutArrayQualifier(@SuppressWarnings("rawtypes") Class clazz) {\r
+                       String simpleName = clazz.getSimpleName();\r
+                       int indexOfBracket = simpleName.indexOf('['); \r
+                       if (indexOfBracket != -1)\r
+                               return simpleName.substring(0, indexOfBracket);\r
+                       return simpleName;\r
+               }\r
+}\r
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java
new file mode 100644 (file)
index 0000000..66e9c6b
--- /dev/null
@@ -0,0 +1,94 @@
+/*\r
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>\r
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; version 2 of the License.\r
+ *\r
+ * This program is distributed in the hope that it will be useful, but\r
+ * WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+ * General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License along\r
+ * with this program; if not, write to the Free Software Foundation, Inc.,\r
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.\r
+ */\r
+\r
+\r
+package org.altusmetrum.AltosDroid;\r
+\r
+import java.text.*;\r
+import java.io.*;\r
+import java.util.concurrent.*;\r
+import android.util.Log;\r
+import android.os.Handler;\r
+\r
+import org.altusmetrum.AltosLib.*;\r
+\r
+\r
+public class TelemetryReader extends Thread {\r
+\r
+       private static final String TAG = "TelemetryReader";\r
+\r
+       int         crc_errors;\r
+\r
+       Handler     handler;\r
+\r
+       AltosLink   link;\r
+       AltosRecord previous;\r
+\r
+       LinkedBlockingQueue<AltosLine> telem;\r
+\r
+       public AltosRecord read() throws ParseException, AltosCRCException, InterruptedException, IOException {\r
+               AltosLine l = telem.take();\r
+               if (l.line == null)\r
+                       throw new IOException("IO error");\r
+               AltosRecord     next = AltosTelemetry.parse(l.line, previous);\r
+               previous = next;\r
+               return next;\r
+       }\r
+\r
+       public void close() {\r
+               previous = null;\r
+               link.remove_monitor(telem);\r
+               link = null;\r
+               telem.clear();\r
+               telem = null;\r
+       }\r
+\r
+       public void run() {\r
+               AltosState  state = null;\r
+\r
+               try {\r
+                       for (;;) {\r
+                               try {\r
+                                       AltosRecord record = read();\r
+                                       if (record == null)\r
+                                               break;\r
+                                       state = new AltosState(record, state);\r
+\r
+                                       handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();\r
+                               } catch (ParseException pp) {\r
+                                       Log.e(TAG, String.format("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage()));\r
+                               } catch (AltosCRCException ce) {\r
+                                       ++crc_errors;\r
+                               }\r
+                       }\r
+               } catch (InterruptedException ee) {\r
+               } catch (IOException ie) {\r
+               } finally {\r
+                       close();\r
+               }\r
+       }\r
+\r
+       public TelemetryReader (AltosLink in_link, Handler in_handler) {\r
+               link    = in_link;\r
+               handler = in_handler;\r
+\r
+               previous = null;\r
+               telem = new LinkedBlockingQueue<AltosLine>();\r
+               link.add_monitor(telem);\r
+       }\r
+}\r
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java b/altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java
new file mode 100644 (file)
index 0000000..393fd2f
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright © 2012 Mike Beattie <mike@ethernal.org>
+ *
+ * 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 org.altusmetrum.AltosDroid;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.concurrent.TimeoutException;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.app.Notification;
+//import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.bluetooth.BluetoothDevice;
+import android.content.Intent;
+//import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+import android.widget.Toast;
+
+import org.altusmetrum.AltosLib.*;
+
+public class TelemetryService extends Service {
+
+       private static final String TAG = "TelemetryService";
+       private static final boolean D = true;
+
+       static final int MSG_REGISTER_CLIENT   = 1;
+       static final int MSG_UNREGISTER_CLIENT = 2;
+       static final int MSG_CONNECT           = 3;
+       static final int MSG_CONNECTED         = 4;
+       static final int MSG_CONNECT_FAILED    = 5;
+       static final int MSG_DISCONNECTED      = 6;
+       static final int MSG_TELEMETRY         = 7;
+       static final int MSG_SETFREQUENCY      = 8;
+
+       public static final int STATE_NONE       = 0;
+       public static final int STATE_READY      = 1;
+       public static final int STATE_CONNECTING = 2;
+       public static final int STATE_CONNECTED  = 3;
+
+       // Unique Identification Number for the Notification.
+       // We use it on Notification start, and to cancel it.
+       private int NOTIFICATION = R.string.telemetry_service_label;
+       //private NotificationManager mNM;
+
+       // Timer - we wake up every now and then to decide if the service should stop
+       private Timer timer = new Timer();
+
+       ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
+       final Handler   mHandler   = new IncomingHandler(this);
+       final Messenger mMessenger = new Messenger(mHandler); // Target we publish for clients to send messages to IncomingHandler.
+
+       // Name of the connected device
+       private BluetoothDevice device           = null;
+       private AltosBluetooth  mAltosBluetooth  = null;
+       private AltosConfigData mConfigData      = null;
+       private TelemetryReader mTelemetryReader = null;
+
+       // internally track state of bluetooth connection
+       private int state = STATE_NONE;
+
+       // Handler of incoming messages from clients.
+       static class IncomingHandler extends Handler {
+               private final WeakReference<TelemetryService> service;
+               IncomingHandler(TelemetryService s) { service = new WeakReference<TelemetryService>(s); }
+
+               @Override
+               public void handleMessage(Message msg) {
+                       TelemetryService s = service.get();
+                       switch (msg.what) {
+                       case MSG_REGISTER_CLIENT:
+                               s.mClients.add(msg.replyTo);
+                               try {
+                                       // Now we try to send the freshly connected UI any relavant information about what
+                                       // we're talking to - Basically state and Config Data.
+                                       msg.replyTo.send(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, s.state, -1, s.mConfigData));
+                               } catch (RemoteException e) {
+                                       s.mClients.remove(msg.replyTo);
+                               }
+                               if (D) Log.d(TAG, "Client bound to service");
+                               break;
+                       case MSG_UNREGISTER_CLIENT:
+                               s.mClients.remove(msg.replyTo);
+                               if (D) Log.d(TAG, "Client unbound from service");
+                               break;
+                       case MSG_CONNECT:
+                               if (D) Log.d(TAG, "Connect command received");
+                               s.device = (BluetoothDevice) msg.obj;
+                               s.startAltosBluetooth();
+                               break;
+                       case MSG_CONNECTED:
+                               if (D) Log.d(TAG, "Connected to device");
+                               s.connected();
+                               break;
+                       case MSG_CONNECT_FAILED:
+                               if (D) Log.d(TAG, "Connection failed... retrying");
+                               s.startAltosBluetooth();
+                               break;
+                       case MSG_DISCONNECTED:
+                               // Only do the following if we haven't been shutdown elsewhere..
+                               if (s.device != null) {
+                                       if (D) Log.d(TAG, "Disconnected from " + s.device.getName());
+                                       s.stopAltosBluetooth();
+                               }
+                               break;
+                       case MSG_TELEMETRY:
+                               s.sendMessageToClients(Message.obtain(null, AltosDroid.MSG_TELEMETRY, msg.obj));
+                               break;
+                       case MSG_SETFREQUENCY:
+                               if (s.state == STATE_CONNECTED) {
+                                       try {
+                                               s.mAltosBluetooth.set_radio_frequency((Double) msg.obj);
+                                       } catch (InterruptedException e) {
+                                       } catch (TimeoutException e) {
+                                       }
+                               }
+                               break;
+                       default:
+                               super.handleMessage(msg);
+                       }
+               }
+       }
+
+       private void sendMessageToClients(Message m) {
+               for (int i=mClients.size()-1; i>=0; i--) {
+                       try {
+                               mClients.get(i).send(m);
+                       } catch (RemoteException e) {
+                               mClients.remove(i);
+                       }
+               }
+       }
+
+       private void stopAltosBluetooth() {
+               if (D) Log.d(TAG, "stopAltosBluetooth(): begin");
+               setState(STATE_READY);
+               if (mTelemetryReader != null) {
+                       if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryReader");
+                       mTelemetryReader.interrupt();
+                       try {
+                               mTelemetryReader.join();
+                       } catch (InterruptedException e) {
+                       }
+                       mTelemetryReader = null;
+               }
+               if (mAltosBluetooth != null) {
+                       if (D) Log.d(TAG, "stopAltosBluetooth(): stopping AltosBluetooth");
+                       mAltosBluetooth.close();
+                       mAltosBluetooth = null;
+               }
+               device = null;
+               mConfigData = null;
+       }
+
+       private void startAltosBluetooth() {
+               if (mAltosBluetooth == null) {
+                       if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
+                       mAltosBluetooth = new AltosBluetooth(device, mHandler);
+                       setState(STATE_CONNECTING);
+               } else {
+                       // This is a bit of a hack - if it appears we're still connected, we treat this as a restart.
+                       // So, to give a suitable delay to teardown/bringup, we just schedule a resend of a message
+                       // to ourselves in a few seconds time that will ultimately call this method again.
+                       // ... then we tear down the existing connection.
+                       // We do it this way around so that we don't lose a reference to the device when this method
+                       // is called on reception of MSG_CONNECT_FAILED in the handler above.
+                       mHandler.sendMessageDelayed(Message.obtain(null, MSG_CONNECT, device), 3000);
+                       stopAltosBluetooth();
+               }
+       }
+
+       private synchronized void setState(int s) {
+               if (D) Log.d(TAG, "setState(): " + state + " -> " + s);
+               state = s;
+
+               // This shouldn't be required - mConfigData should be null for any non-connected
+               // state, but to be safe and to reduce message size
+               AltosConfigData acd = (state == STATE_CONNECTED) ? mConfigData : null;
+
+               sendMessageToClients(Message.obtain(null, AltosDroid.MSG_STATE_CHANGE, state, -1, acd));
+       }
+
+       private void connected() {
+               try {
+                       mConfigData = mAltosBluetooth.config_data();
+               } catch (InterruptedException e) {
+               } catch (TimeoutException e) {
+                       // If this timed out, then we really want to retry it, but
+                       // probably safer to just retry the connection from scratch.
+                       mHandler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
+                       return;
+               }
+
+               setState(STATE_CONNECTED);
+
+               mTelemetryReader = new TelemetryReader(mAltosBluetooth, mHandler);
+               mTelemetryReader.start();
+       }
+
+
+       private void onTimerTick() {
+               if (D) Log.d(TAG, "Timer wakeup");
+               try {
+                       if (mClients.size() <= 0 && state != STATE_CONNECTED) {
+                               stopSelf();
+                       }
+               } catch (Throwable t) {
+                       Log.e(TAG, "Timer failed: ", t);
+               }
+       }
+
+
+       @Override
+       public void onCreate() {
+               // Create a reference to the NotificationManager so that we can update our notifcation text later
+               //mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+
+               setState(STATE_READY);
+
+               // Start our timer - first event in 10 seconds, then every 10 seconds after that.
+               timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 10000L, 10000L);
+
+       }
+
+       @Override
+       public int onStartCommand(Intent intent, int flags, int startId) {
+               Log.i("TelemetryService", "Received start id " + startId + ": " + intent);
+
+               CharSequence text = getText(R.string.telemetry_service_started);
+
+               // Create notification to be displayed while the service runs
+               Notification notification = new Notification(R.drawable.am_status_c, text, 0);
+
+               // The PendingIntent to launch our activity if the user selects this notification
+               PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
+                               new Intent(this, AltosDroid.class), 0);
+
+               // Set the info for the views that show in the notification panel.
+               notification.setLatestEventInfo(this, getText(R.string.telemetry_service_label), text, contentIntent);
+
+               // Set the notification to be in the "Ongoing" section.
+               notification.flags |= Notification.FLAG_ONGOING_EVENT;
+
+               // Move us into the foreground.
+               startForeground(NOTIFICATION, notification);
+
+               // We want this service to continue running until it is explicitly
+               // stopped, so return sticky.
+               return START_STICKY;
+       }
+
+       @Override
+       public void onDestroy() {
+
+               // Stop the bluetooth Comms threads
+               stopAltosBluetooth();
+
+               // Demote us from the foreground, and cancel the persistent notification.
+               stopForeground(true);
+
+               // Stop our timer
+               if (timer != null) {timer.cancel();}
+
+               // Tell the user we stopped.
+               Toast.makeText(this, R.string.telemetry_service_stopped, Toast.LENGTH_SHORT).show();
+       }
+
+       @Override
+       public IBinder onBind(Intent intent) {
+               return mMessenger.getBinder();
+       }
+
+
+}
diff --git a/altoslib/.gitignore b/altoslib/.gitignore
new file mode 100644 (file)
index 0000000..a71d076
--- /dev/null
@@ -0,0 +1,3 @@
+bin
+classAltosLib.stamp
+AltosLib.jar
diff --git a/altoslib/AltosAccel.java b/altoslib/AltosAccel.java
new file mode 100644 (file)
index 0000000..d14764a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosAccel extends AltosUnits {
+
+       public double value(double v) {
+               if (AltosConvert.imperial_units)
+                       return AltosConvert.meters_to_feet(v);
+               return v;
+       }
+
+       public String show_units() {
+               if (AltosConvert.imperial_units)
+                       return "ft/s²";
+               return "m/s²";
+       }
+
+       public String say_units() {
+               if (AltosConvert.imperial_units)
+                       return "feet per second squared";
+               return "meters per second squared";
+       }
+
+       int show_fraction(int width) {
+               return width / 9;
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosCRCException.java b/altoslib/AltosCRCException.java
new file mode 100644 (file)
index 0000000..101c536
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+public class AltosCRCException extends Exception {
+       public int rssi;
+
+       public AltosCRCException (int in_rssi) {
+               rssi = in_rssi;
+       }
+}
diff --git a/altoslib/AltosConfigData.java b/altoslib/AltosConfigData.java
new file mode 100644 (file)
index 0000000..6f34363
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+import java.util.*;
+import java.text.*;
+import java.util.concurrent.*;
+
+public class AltosConfigData implements Iterable<String> {
+
+       /* Version information */
+       public String   manufacturer;
+       public String   product;
+       public String   version;
+       public int      log_format;
+       public int      serial;
+
+       /* Strings returned */
+       public LinkedList<String>       lines;
+
+       /* Config information */
+       public int      config_major;
+       public int      config_minor;
+       public int      main_deploy;
+       public int      apogee_delay;
+       public int      radio_channel;
+       public int      radio_setting;
+       public int      radio_frequency;
+       public String   callsign;
+       public int      accel_cal_plus, accel_cal_minus;
+       public int      radio_calibration;
+       public int      flight_log_max;
+       public int      ignite_mode;
+       public int      stored_flight;
+       public int      storage_size;
+       public int      storage_erase_unit;
+
+       public static String get_string(String line, String label) throws  ParseException {
+               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);
+                       return quoted;
+               }
+               throw new ParseException("mismatch", 0);
+       }
+
+       public static int get_int(String line, String label) throws NumberFormatException, ParseException {
+               if (line.startsWith(label)) {
+                       String tail = line.substring(label.length()).trim();
+                       String[] tokens = tail.split("\\s+");
+                       if (tokens.length > 0)
+                               return  Integer.parseInt(tokens[0]);
+               }
+               throw new ParseException("mismatch", 0);
+       }
+
+       public Iterator<String> iterator() {
+               return lines.iterator();
+       }
+
+       public int log_available() {
+               switch (log_format) {
+               case AltosLib.AO_LOG_FORMAT_TINY:
+                       if (stored_flight == 0)
+                               return 1;
+                       return 0;
+               default:
+                       if (flight_log_max <= 0)
+                               return 1;
+                       int     log_space = storage_size - storage_erase_unit;
+                       int     log_used = stored_flight * flight_log_max;
+
+                       if (log_used >= log_space)
+                               return 0;
+                       return (log_space - log_used) / flight_log_max;
+               }
+       }
+
+       int[] parse_version(String v) {
+               String[] parts = v.split("\\.");
+               int r[] = new int[parts.length];
+
+               for (int i = 0; i < parts.length; i++) {
+                       try {
+                               r[i] = AltosLib.fromdec(parts[i]);
+                       } catch (NumberFormatException n) {
+                               r[i] = 0;
+                       }
+               }
+
+               return r;
+       }
+       
+       public int compare_version(String other) {
+               int[]   me = parse_version(version);
+               int[]   them = parse_version(other);
+
+               int     l = Math.min(me.length, them.length);
+
+               for (int i = 0; i < l; i++) {
+                       int     d = me[i] - them[i];
+                       if (d != 0)
+                               return d;
+               }
+               if (me.length > l)
+                       return 1;
+               if (them.length > l)
+                       return -1;
+               return 0;
+       }
+
+       public AltosConfigData(AltosLink link) throws InterruptedException, TimeoutException {
+               link.printf("c s\nf\nl\nv\n");
+               lines = new LinkedList<String>();
+               radio_setting = 0;
+               radio_frequency = 0;
+               stored_flight = 0;
+               for (;;) {
+                       String line = link.get_reply();
+                       if (line == null)
+                               throw new TimeoutException();
+                       if (line.contains("Syntax error"))
+                               continue;
+                       lines.add(line);
+                       try { serial = get_int(line, "serial-number"); } catch (Exception e) {}
+                       try { log_format = get_int(line, "log-format"); } catch (Exception e) {}
+                       try { main_deploy = get_int(line, "Main deploy:"); } catch (Exception e) {}
+                       try { apogee_delay = get_int(line, "Apogee delay:"); } catch (Exception e) {}
+                       try { radio_channel = get_int(line, "Radio channel:"); } catch (Exception e) {}
+                       try { radio_setting = get_int(line, "Radio setting:"); } catch (Exception e) {}
+                       try {
+                               radio_frequency = get_int(line, "Frequency:");
+                               if (radio_frequency < 0)
+                                       radio_frequency = 434550;
+                       } catch (Exception e) {}
+                       try {
+                               if (line.startsWith("Accel cal")) {
+                                       String[] bits = line.split("\\s+");
+                                       if (bits.length >= 6) {
+                                               accel_cal_plus = Integer.parseInt(bits[3]);
+                                               accel_cal_minus = Integer.parseInt(bits[5]);
+                                       }
+                               }
+                       } catch (Exception e) {}
+                       try { radio_calibration = get_int(line, "Radio cal:"); } catch (Exception e) {}
+                       try { flight_log_max = get_int(line, "Max flight log:"); } catch (Exception e) {}
+                       try { ignite_mode = get_int(line, "Ignite mode:"); } catch (Exception e) {}
+                       try { callsign = get_string(line, "Callsign:"); } catch (Exception e) {}
+                       try { version = get_string(line,"software-version"); } catch (Exception e) {}
+                       try { product = get_string(line,"product"); } catch (Exception e) {}
+                       try { manufacturer = get_string(line,"manufacturer"); } catch (Exception e) {}
+
+                       try { get_int(line, "flight"); stored_flight++; }  catch (Exception e) {}
+                       try { storage_size = get_int(line, "Storage size:"); } catch (Exception e) {}
+                       try { storage_erase_unit = get_int(line, "Storage erase unit"); } catch (Exception e) {}
+
+                       /* signals the end of the version info */
+                       if (line.startsWith("software-version"))
+                               break;
+               }
+       }
+
+}
\ No newline at end of file
diff --git a/altoslib/AltosConvert.java b/altoslib/AltosConvert.java
new file mode 100644 (file)
index 0000000..acd6c5f
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+/*
+ * Sensor data conversion functions
+ */
+package org.altusmetrum.AltosLib;
+
+public class AltosConvert {
+       /*
+        * Pressure Sensor Model, version 1.1
+        *
+        * written by Holly Grimes
+        *
+        * Uses the International Standard Atmosphere as described in
+        *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
+        *    from the Portland State Aerospace Society, except that the atmosphere
+        *    is divided into layers with each layer having a different lapse rate.
+        *
+        * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
+        *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
+        *
+        * Height measurements use the local tangent plane.  The postive z-direction is up.
+        *
+        * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
+        *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
+        *   in Joules/(kilogram-Kelvin).
+        */
+
+       public static final double GRAVITATIONAL_ACCELERATION = -9.80665;
+       public static final double AIR_GAS_CONSTANT             = 287.053;
+       public static final double NUMBER_OF_LAYERS             = 7;
+       public static final double MAXIMUM_ALTITUDE             = 84852.0;
+       public static final double MINIMUM_PRESSURE             = 0.3734;
+       public static final double LAYER0_BASE_TEMPERATURE      = 288.15;
+       public static final double LAYER0_BASE_PRESSURE = 101325;
+
+       /* lapse rate and base altitude for each layer in the atmosphere */
+       public static final double[] lapse_rate = {
+               -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
+       };
+
+       public static final int[] base_altitude = {
+               0, 11000, 20000, 32000, 47000, 51000, 71000
+       };
+
+       /* outputs atmospheric pressure associated with the given altitude.
+        * altitudes are measured with respect to the mean sea level
+        */
+       public static double
+       altitude_to_pressure(double altitude)
+       {
+               double base_temperature = LAYER0_BASE_TEMPERATURE;
+               double base_pressure = LAYER0_BASE_PRESSURE;
+
+               double pressure;
+               double base; /* base for function to determine pressure */
+               double exponent; /* exponent for function to determine pressure */
+               int layer_number; /* identifies layer in the atmosphere */
+               double delta_z; /* difference between two altitudes */
+
+               if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
+                       return 0;
+
+               /* calculate the base temperature and pressure for the atmospheric layer
+                  associated with the inputted altitude */
+               for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
+                       delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+                       if (lapse_rate[layer_number] == 0.0) {
+                               exponent = GRAVITATIONAL_ACCELERATION * delta_z
+                                       / AIR_GAS_CONSTANT / base_temperature;
+                               base_pressure *= Math.exp(exponent);
+                       }
+                       else {
+                               base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+                               exponent = GRAVITATIONAL_ACCELERATION /
+                                       (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+                               base_pressure *= Math.pow(base, exponent);
+                       }
+                       base_temperature += delta_z * lapse_rate[layer_number];
+               }
+
+               /* calculate the pressure at the inputted altitude */
+               delta_z = altitude - base_altitude[layer_number];
+               if (lapse_rate[layer_number] == 0.0) {
+                       exponent = GRAVITATIONAL_ACCELERATION * delta_z
+                               / AIR_GAS_CONSTANT / base_temperature;
+                       pressure = base_pressure * Math.exp(exponent);
+               }
+               else {
+                       base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+                       exponent = GRAVITATIONAL_ACCELERATION /
+                               (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+                       pressure = base_pressure * Math.pow(base, exponent);
+               }
+
+               return pressure;
+       }
+
+
+/* outputs the altitude associated with the given pressure. the altitude
+   returned is measured with respect to the mean sea level */
+       public static double
+       pressure_to_altitude(double pressure)
+       {
+
+               double next_base_temperature = LAYER0_BASE_TEMPERATURE;
+               double next_base_pressure = LAYER0_BASE_PRESSURE;
+
+               double altitude;
+               double base_pressure;
+               double base_temperature;
+               double base; /* base for function to determine base pressure of next layer */
+               double exponent; /* exponent for function to determine base pressure
+                                   of next layer */
+               double coefficient;
+               int layer_number; /* identifies layer in the atmosphere */
+               int delta_z; /* difference between two altitudes */
+
+               if (pressure < 0)  /* illegal pressure */
+                       return -1;
+               if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
+                       return MAXIMUM_ALTITUDE;
+
+               /* calculate the base temperature and pressure for the atmospheric layer
+                  associated with the inputted pressure. */
+               layer_number = -1;
+               do {
+                       layer_number++;
+                       base_pressure = next_base_pressure;
+                       base_temperature = next_base_temperature;
+                       delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+                       if (lapse_rate[layer_number] == 0.0) {
+                               exponent = GRAVITATIONAL_ACCELERATION * delta_z
+                                       / AIR_GAS_CONSTANT / base_temperature;
+                               next_base_pressure *= Math.exp(exponent);
+                       }
+                       else {
+                               base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+                               exponent = GRAVITATIONAL_ACCELERATION /
+                                       (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+                               next_base_pressure *= Math.pow(base, exponent);
+                       }
+                       next_base_temperature += delta_z * lapse_rate[layer_number];
+               }
+               while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
+
+               /* calculate the altitude associated with the inputted pressure */
+               if (lapse_rate[layer_number] == 0.0) {
+                       coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
+                               * base_temperature;
+                       altitude = base_altitude[layer_number]
+                               + coefficient * Math.log(pressure / base_pressure);
+               }
+               else {
+                       base = pressure / base_pressure;
+                       exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
+                               / GRAVITATIONAL_ACCELERATION;
+                       coefficient = base_temperature / lapse_rate[layer_number];
+                       altitude = base_altitude[layer_number]
+                               + coefficient * (Math.pow(base, exponent) - 1);
+               }
+
+               return altitude;
+       }
+
+       public static double
+       cc_battery_to_voltage(double battery)
+       {
+               return battery / 32767.0 * 5.0;
+       }
+
+       public static double
+       cc_ignitor_to_voltage(double ignite)
+       {
+               return ignite / 32767 * 15.0;
+       }
+
+       public static double radio_to_frequency(int freq, int setting, int cal, int channel) {
+               double  f;
+
+               if (freq > 0)
+                       f = freq / 1000.0;
+               else {
+                       if (setting <= 0)
+                               setting = cal;
+                       f = 434.550 * setting / cal;
+                       /* Round to nearest 50KHz */
+                       f = Math.floor (20.0 * f + 0.5) / 20.0;
+               }
+               return f + channel * 0.100;
+       }
+
+       public static int radio_frequency_to_setting(double frequency, int cal) {
+               double  set = frequency / 434.550 * cal;
+
+               return (int) Math.floor (set + 0.5);
+       }
+
+       public static int radio_frequency_to_channel(double frequency) {
+               int     channel = (int) Math.floor ((frequency - 434.550) / 0.100 + 0.5);
+
+               if (channel < 0)
+                       channel = 0;
+               if (channel > 9)
+                       channel = 9;
+               return channel;
+       }
+
+       public static double radio_channel_to_frequency(int channel) {
+               return 434.550 + channel * 0.100;
+       }
+
+       public static int[] ParseHex(String line) {
+               String[] tokens = line.split("\\s+");
+               int[] array = new int[tokens.length];
+
+               for (int i = 0; i < tokens.length; i++)
+                       try {
+                               array[i] = Integer.parseInt(tokens[i], 16);
+                       } catch (NumberFormatException ne) {
+                               return null;
+                       }
+               return array;
+       }
+
+       public static double meters_to_feet(double meters) {
+               return meters * (100 / (2.54 * 12));
+       }
+
+       public static double meters_to_miles(double meters) {
+               return meters_to_feet(meters) / 5280;
+       }
+
+       public static double meters_to_mph(double mps) {
+               return meters_to_miles(mps) * 3600;
+       }
+
+       public static double meters_to_mach(double meters) {
+               return meters / 343;            /* something close to mach at usual rocket sites */
+       }
+
+       public static double meters_to_g(double meters) {
+               return meters / 9.80665;
+       }
+
+       public static boolean imperial_units = false;
+
+       public static AltosDistance distance = new AltosDistance();
+
+       public static AltosHeight height = new AltosHeight();
+
+       public static AltosSpeed speed = new AltosSpeed();
+
+       public static AltosAccel accel = new AltosAccel();
+
+       public static String show_gs(String format, double a) {
+               a = meters_to_g(a);
+               format = format.concat(" g");
+               return String.format(format, a);
+       }
+
+       public static String say_gs(double a) {
+               return String.format("%6.0 gees", meters_to_g(a));
+       }
+
+       public static int checksum(int[] data, int start, int length) {
+               int     csum = 0x5a;
+               for (int i = 0; i < length; i++)
+                       csum += data[i + start];
+               return csum & 0xff;
+       }
+}
diff --git a/altoslib/AltosDistance.java b/altoslib/AltosDistance.java
new file mode 100644 (file)
index 0000000..a5e7331
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosDistance extends AltosUnits {
+
+       public double value(double v) {
+               if (AltosConvert.imperial_units)
+                       return AltosConvert.meters_to_miles(v);
+               return v;
+       }
+
+       public String show_units() {
+               if (AltosConvert.imperial_units)
+                       return "miles";
+               return "m";
+       }
+
+       public String say_units() {
+               if (AltosConvert.imperial_units)
+                       return "miles";
+               return "meters";
+       }
+
+       int show_fraction(int width) {
+               if (AltosConvert.imperial_units)
+                       return width / 3;
+               return width / 9;
+       }
+
+       int say_fraction() {
+               return 1;
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosEepromChunk.java b/altoslib/AltosEepromChunk.java
new file mode 100644 (file)
index 0000000..77b22fe
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+import java.util.concurrent.*;
+
+public class AltosEepromChunk {
+
+       public static final int chunk_size = 256;
+       public static final int per_line = 8;
+
+       public int              data[];
+       public int              address;
+       public ParseException   parse_exception = null;
+
+       int[] ParseHex(String line) {
+               String[] tokens = line.split("\\s+");
+               int[] array = new int[tokens.length];
+
+               for (int i = 0; i < tokens.length; i++)
+                       try {
+                               array[i] = Integer.parseInt(tokens[i], 16);
+                       } catch (NumberFormatException ne) {
+                               return null;
+                       }
+               return array;
+       }
+
+       public int data(int offset) {
+               return data[offset];
+       }
+
+       public int data16(int offset) {
+               return data[offset] | (data[offset + 1] << 8);
+       }
+
+       public int data32(int offset) {
+               return data[offset] | (data[offset + 1] << 8) |
+                       (data[offset+2] << 16) | (data[offset+3] << 24);
+       }
+
+       public boolean erased(int start, int len) {
+               for (int i = 0; i < len; i++)
+                       if (data[start+i] != 0xff)
+                               return false;
+               return true;
+       }
+
+       public AltosEepromChunk(AltosLink link, int block, boolean flush)
+               throws TimeoutException, InterruptedException {
+
+               int     offset;
+
+               data = new int[chunk_size];
+               address = block * chunk_size;
+               if (flush)
+                       link.flush_input();
+               link.printf("e %x\n", block);
+
+               for (offset = 0; offset < chunk_size; offset += per_line) {
+                       try {
+                               String  line = link.get_reply(5000);
+
+                               if (line == null)
+                                       throw new TimeoutException();
+
+                               int[] values = ParseHex(line);
+
+                               if (values == null || values.length != per_line + 1)
+                                       throw new ParseException(String.format("invalid line %s", line), 0);
+                               if (values[0] != offset)
+                                       throw new ParseException(String.format("data address out of sync at 0x%x",
+                                                                              address + offset), 0);
+                               for (int i = 0; i < per_line; i++)
+                                       data[offset + i] = values[1 + i];
+                       } catch (ParseException pe) {
+                               for (int i = 0; i < per_line; i++)
+                                       data[offset + i] = 0xff;
+                               if (parse_exception == null)
+                                       parse_exception = pe;
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosEepromIterable.java b/altoslib/AltosEepromIterable.java
new file mode 100644 (file)
index 0000000..1aa816e
--- /dev/null
@@ -0,0 +1,429 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+
+public class AltosEepromIterable extends AltosRecordIterable {
+
+       static final int        seen_flight = 1;
+       static final int        seen_sensor = 2;
+       static final int        seen_temp_volt = 4;
+       static final int        seen_deploy = 8;
+       static final int        seen_gps_time = 16;
+       static final int        seen_gps_lat = 32;
+       static final int        seen_gps_lon = 64;
+
+       static final int        seen_basic = seen_flight|seen_sensor;
+
+       boolean                 has_accel;
+       boolean                 has_gps;
+       boolean                 has_ignite;
+
+       AltosEepromRecord       flight_record;
+       AltosEepromRecord       gps_date_record;
+
+       TreeSet<AltosOrderedRecord>     records;
+
+       LinkedList<AltosRecord> list;
+
+       class EepromState {
+               int     seen;
+               int     n_pad_samples;
+               double  ground_pres;
+               int     gps_tick;
+               int     boost_tick;
+               int     sensor_tick;
+
+               EepromState() {
+                       seen = 0;
+                       n_pad_samples = 0;
+                       ground_pres = 0.0;
+                       gps_tick = 0;
+               }
+       }
+
+       void update_state(AltosRecordTM state, AltosEepromRecord record, EepromState eeprom) {
+               state.tick = record.tick;
+               switch (record.cmd) {
+               case AltosLib.AO_LOG_FLIGHT:
+                       eeprom.seen |= seen_flight;
+                       state.ground_accel = record.a;
+                       state.flight_accel = record.a;
+                       state.flight = record.b;
+                       eeprom.boost_tick = record.tick;
+                       break;
+               case AltosLib.AO_LOG_SENSOR:
+                       state.accel = record.a;
+                       state.pres = record.b;
+                       if (state.state < AltosLib.ao_flight_boost) {
+                               eeprom.n_pad_samples++;
+                               eeprom.ground_pres += state.pres;
+                               state.ground_pres = (int) (eeprom.ground_pres / eeprom.n_pad_samples);
+                               state.flight_pres = state.ground_pres;
+                       } else {
+                               state.flight_pres = (state.flight_pres * 15 + state.pres) / 16;
+                       }
+                       state.flight_accel = (state.flight_accel * 15 + state.accel) / 16;
+                       if ((eeprom.seen & seen_sensor) == 0)
+                               eeprom.sensor_tick = record.tick - 1;
+                       state.flight_vel += (state.accel_plus_g - state.accel) * (record.tick - eeprom.sensor_tick);
+                       eeprom.seen |= seen_sensor;
+                       eeprom.sensor_tick = record.tick;
+                       has_accel = true;
+                       break;
+               case AltosLib.AO_LOG_PRESSURE:
+                       state.pres = record.b;
+                       state.flight_pres = state.pres;
+                       if (eeprom.n_pad_samples == 0) {
+                               eeprom.n_pad_samples++;
+                               state.ground_pres = state.pres;
+                       }
+                       eeprom.seen |= seen_sensor;
+                       break;
+               case AltosLib.AO_LOG_TEMP_VOLT:
+                       state.temp = record.a;
+                       state.batt = record.b;
+                       eeprom.seen |= seen_temp_volt;
+                       break;
+               case AltosLib.AO_LOG_DEPLOY:
+                       state.drogue = record.a;
+                       state.main = record.b;
+                       eeprom.seen |= seen_deploy;
+                       has_ignite = true;
+                       break;
+               case AltosLib.AO_LOG_STATE:
+                       state.state = record.a;
+                       break;
+               case AltosLib.AO_LOG_GPS_TIME:
+                       eeprom.gps_tick = state.tick;
+                       AltosGPS old = state.gps;
+                       state.gps = new AltosGPS();
+
+                       /* GPS date doesn't get repeated through the file */
+                       if (old != null) {
+                               state.gps.year = old.year;
+                               state.gps.month = old.month;
+                               state.gps.day = old.day;
+                       }
+                       state.gps.hour = (record.a & 0xff);
+                       state.gps.minute = (record.a >> 8);
+                       state.gps.second = (record.b & 0xff);
+
+                       int flags = (record.b >> 8);
+                       state.gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0;
+                       state.gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0;
+                       state.gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >>
+                               AltosLib.AO_GPS_NUM_SAT_SHIFT;
+                       state.new_gps = true;
+                       has_gps = true;
+                       break;
+               case AltosLib.AO_LOG_GPS_LAT:
+                       int lat32 = record.a | (record.b << 16);
+                       state.gps.lat = (double) lat32 / 1e7;
+                       break;
+               case AltosLib.AO_LOG_GPS_LON:
+                       int lon32 = record.a | (record.b << 16);
+                       state.gps.lon = (double) lon32 / 1e7;
+                       break;
+               case AltosLib.AO_LOG_GPS_ALT:
+                       state.gps.alt = record.a;
+                       break;
+               case AltosLib.AO_LOG_GPS_SAT:
+                       if (state.tick == eeprom.gps_tick) {
+                               int svid = record.a;
+                               int c_n0 = record.b >> 8;
+                               state.gps.add_sat(svid, c_n0);
+                       }
+                       break;
+               case AltosLib.AO_LOG_GPS_DATE:
+                       state.gps.year = (record.a & 0xff) + 2000;
+                       state.gps.month = record.a >> 8;
+                       state.gps.day = record.b & 0xff;
+                       break;
+
+               case AltosLib.AO_LOG_CONFIG_VERSION:
+                       break;
+               case AltosLib.AO_LOG_MAIN_DEPLOY:
+                       break;
+               case AltosLib.AO_LOG_APOGEE_DELAY:
+                       break;
+               case AltosLib.AO_LOG_RADIO_CHANNEL:
+                       break;
+               case AltosLib.AO_LOG_CALLSIGN:
+                       state.callsign = record.data;
+                       break;
+               case AltosLib.AO_LOG_ACCEL_CAL:
+                       state.accel_plus_g = record.a;
+                       state.accel_minus_g = record.b;
+                       break;
+               case AltosLib.AO_LOG_RADIO_CAL:
+                       break;
+               case AltosLib.AO_LOG_MANUFACTURER:
+                       break;
+               case AltosLib.AO_LOG_PRODUCT:
+                       break;
+               case AltosLib.AO_LOG_SERIAL_NUMBER:
+                       state.serial = record.a;
+                       break;
+               case AltosLib.AO_LOG_SOFTWARE_VERSION:
+                       break;
+               }
+               state.seen |= eeprom.seen;
+       }
+
+       LinkedList<AltosRecord> make_list() {
+               LinkedList<AltosRecord>         list = new LinkedList<AltosRecord>();
+               Iterator<AltosOrderedRecord>    iterator = records.iterator();
+               AltosOrderedRecord              record = null;
+               AltosRecordTM                   state = new AltosRecordTM();
+               //boolean                               last_reported = false;
+               EepromState                     eeprom = new EepromState();
+
+               state.state = AltosLib.ao_flight_pad;
+               state.accel_plus_g = 15758;
+               state.accel_minus_g = 16294;
+
+               /* Pull in static data from the flight and gps_date records */
+               if (flight_record != null)
+                       update_state(state, flight_record, eeprom);
+               if (gps_date_record != null)
+                       update_state(state, gps_date_record, eeprom);
+
+               while (iterator.hasNext()) {
+                       record = iterator.next();
+                       if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) {
+                               AltosRecordTM r = state.clone();
+                               r.time = (r.tick - eeprom.boost_tick) / 100.0;
+                               list.add(r);
+                       }
+                       update_state(state, record, eeprom);
+               }
+               AltosRecordTM r = state.clone();
+               r.time = (r.tick - eeprom.boost_tick) / 100.0;
+               list.add(r);
+       return list;
+       }
+
+       public Iterator<AltosRecord> iterator() {
+               if (list == null)
+                       list = make_list();
+               return list.iterator();
+       }
+
+       public boolean has_gps() { return has_gps; }
+       public boolean has_accel() { return has_accel; }
+       public boolean has_ignite() { return has_ignite; }
+
+       public void write_comments(PrintStream out) {
+               Iterator<AltosOrderedRecord>    iterator = records.iterator();
+               out.printf("# Comments\n");
+               while (iterator.hasNext()) {
+                       AltosOrderedRecord      record = iterator.next();
+                       switch (record.cmd) {
+                       case AltosLib.AO_LOG_CONFIG_VERSION:
+                               out.printf("# Config version: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_MAIN_DEPLOY:
+                               out.printf("# Main deploy: %s\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_APOGEE_DELAY:
+                               out.printf("# Apogee delay: %s\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_RADIO_CHANNEL:
+                               out.printf("# Radio channel: %s\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_CALLSIGN:
+                               out.printf("# Callsign: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_ACCEL_CAL:
+                               out.printf ("# Accel cal: %d %d\n", record.a, record.b);
+                               break;
+                       case AltosLib.AO_LOG_RADIO_CAL:
+                               out.printf ("# Radio cal: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_MAX_FLIGHT_LOG:
+                               out.printf ("# Max flight log: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_MANUFACTURER:
+                               out.printf ("# Manufacturer: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_PRODUCT:
+                               out.printf ("# Product: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_SERIAL_NUMBER:
+                               out.printf ("# Serial number: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_SOFTWARE_VERSION:
+                               out.printf ("# Software version: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_BARO_RESERVED:
+                               out.printf ("# Baro reserved: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_SENS:
+                               out.printf ("# Baro sens: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_OFF:
+                               out.printf ("# Baro off: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TCS:
+                               out.printf ("# Baro tcs: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TCO:
+                               out.printf ("# Baro tco: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TREF:
+                               out.printf ("# Baro tref: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TEMPSENS:
+                               out.printf ("# Baro tempsens: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_CRC:
+                               out.printf ("# Baro crc: %d\n", record.a);
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * Given an AO_LOG_GPS_TIME record with correct time, and one
+        * missing time, rewrite the missing time values with the good
+        * ones, assuming that the difference between them is 'diff' seconds
+        */
+       void update_time(AltosOrderedRecord good, AltosOrderedRecord bad) {
+
+               int diff = (bad.tick - good.tick + 50) / 100;
+
+               int hour = (good.a & 0xff);
+               int minute = (good.a >> 8);
+               int second = (good.b & 0xff);
+               int flags = (good.b >> 8);
+               int seconds = hour * 3600 + minute * 60 + second;
+
+               /* Make sure this looks like a good GPS value */
+               if ((flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> AltosLib.AO_GPS_NUM_SAT_SHIFT < 4)
+                       flags = (flags & ~AltosLib.AO_GPS_NUM_SAT_MASK) | (4 << AltosLib.AO_GPS_NUM_SAT_SHIFT);
+               flags |= AltosLib.AO_GPS_RUNNING;
+               flags |= AltosLib.AO_GPS_VALID;
+
+               int new_seconds = seconds + diff;
+               if (new_seconds < 0)
+                       new_seconds += 24 * 3600;
+               int new_second = (new_seconds % 60);
+               int new_minutes = (new_seconds / 60);
+               int new_minute = (new_minutes % 60);
+               int new_hours = (new_minutes / 60);
+               int new_hour = (new_hours % 24);
+
+               bad.a = new_hour + (new_minute << 8);
+               bad.b = new_second + (flags << 8);
+       }
+
+       /*
+        * Read the whole file, dumping records into a RB tree so
+        * we can enumerate them in time order -- the eeprom data
+        * are sometimes out of order with GPS data getting timestamps
+        * matching the first packet out of the GPS unit but not
+        * written until the final GPS packet has been received.
+        */
+       public AltosEepromIterable (FileInputStream input) {
+               records = new TreeSet<AltosOrderedRecord>();
+
+               AltosOrderedRecord last_gps_time = null;
+
+               int index = 0;
+               int prev_tick = 0;
+               boolean prev_tick_valid = false;
+               boolean missing_time = false;
+
+               try {
+                       for (;;) {
+                               String line = AltosLib.gets(input);
+                               if (line == null)
+                                       break;
+                               AltosOrderedRecord record = new AltosOrderedRecord(line, index++, prev_tick, prev_tick_valid);
+                               if (record.cmd == AltosLib.AO_LOG_INVALID)
+                                       continue;
+                               prev_tick = record.tick;
+                               if (record.cmd < AltosLib.AO_LOG_CONFIG_VERSION)
+                                       prev_tick_valid = true;
+                               if (record.cmd == AltosLib.AO_LOG_FLIGHT) {
+                                       flight_record = record;
+                                       continue;
+                               }
+
+                               /* Two firmware bugs caused the loss of some GPS data.
+                                * The flight date would never be recorded, and often
+                                * the flight time would get overwritten by another
+                                * record. Detect the loss of the GPS date and fix up the
+                                * missing time records
+                                */
+                               if (record.cmd == AltosLib.AO_LOG_GPS_DATE) {
+                                       gps_date_record = record;
+                                       continue;
+                               }
+
+                               /* go back and fix up any missing time values */
+                               if (record.cmd == AltosLib.AO_LOG_GPS_TIME) {
+                                       last_gps_time = record;
+                                       if (missing_time) {
+                                               Iterator<AltosOrderedRecord> iterator = records.iterator();
+                                               while (iterator.hasNext()) {
+                                                       AltosOrderedRecord old = iterator.next();
+                                                       if (old.cmd == AltosLib.AO_LOG_GPS_TIME &&
+                                                           old.a == -1 && old.b == -1)
+                                                       {
+                                                               update_time(record, old);
+                                                       }
+                                               }
+                                               missing_time = false;
+                                       }
+                               }
+
+                               if (record.cmd == AltosLib.AO_LOG_GPS_LAT) {
+                                       if (last_gps_time == null || last_gps_time.tick != record.tick) {
+                                               AltosOrderedRecord add_gps_time = new AltosOrderedRecord(AltosLib.AO_LOG_GPS_TIME,
+                                                                                                        record.tick,
+                                                                                                        -1, -1, index-1);
+                                               if (last_gps_time != null)
+                                                       update_time(last_gps_time, add_gps_time);
+                                               else
+                                                       missing_time = true;
+
+                                               records.add(add_gps_time);
+                                               record.index = index++;
+                                       }
+                               }
+                               records.add(record);
+
+                               /* Bail after reading the 'landed' record; we're all done */
+                               if (record.cmd == AltosLib.AO_LOG_STATE &&
+                                   record.a == AltosLib.ao_flight_landed)
+                                       break;
+                       }
+               } catch (IOException io) {
+               } catch (ParseException pe) {
+               }
+               try {
+                       input.close();
+               } catch (IOException ie) {
+               }
+       }
+}
diff --git a/altoslib/AltosEepromLog.java b/altoslib/AltosEepromLog.java
new file mode 100644 (file)
index 0000000..211fd70
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+import java.util.concurrent.*;
+
+/*
+ * Extract a bit of information from an eeprom-stored flight log.
+ */
+
+public class AltosEepromLog {
+       public int              serial;
+       public boolean          has_flight;
+       public int              flight;
+       public int              start_block;
+       public int              end_block;
+
+       public int              year, month, day;
+
+       public boolean          selected;
+
+       public AltosEepromLog(AltosConfigData config_data,
+                             AltosLink link,
+                             int in_flight, int in_start_block,
+                             int in_end_block)
+               throws InterruptedException, TimeoutException {
+
+               int             block;
+               boolean         has_date = false;
+
+               flight = in_flight;
+               if (flight != 0)
+                       has_flight = true;
+               start_block = in_start_block;
+               end_block = in_end_block;
+               serial = config_data.serial;
+
+               /*
+                * Select all flights for download
+                */
+               selected = true;
+
+               /*
+                * Look in TeleMetrum log data for date
+                */
+               if (config_data.log_format == AltosLib.AO_LOG_FORMAT_UNKNOWN ||
+                   config_data.log_format == AltosLib.AO_LOG_FORMAT_FULL)
+               {
+                       /*
+                        * Only look in the first two blocks so that this
+                        * process doesn't take a long time
+                        */
+                       if (in_end_block > in_start_block + 2)
+                               in_end_block = in_start_block + 2;
+
+                       for (block = in_start_block; block < in_end_block; block++) {
+                               AltosEepromChunk eechunk = new AltosEepromChunk(link, block, block == in_start_block);
+
+                               for (int i = 0; i < AltosEepromChunk.chunk_size; i += AltosEepromRecord.record_length) {
+                                       try {
+                                               AltosEepromRecord r = new AltosEepromRecord(eechunk, i);
+
+                                               if (r.cmd == AltosLib.AO_LOG_FLIGHT) {
+                                                       flight = r.b;
+                                                       has_flight = true;
+                                               }
+                                               if (r.cmd == AltosLib.AO_LOG_GPS_DATE) {
+                                                       year = 2000 + (r.a & 0xff);
+                                                       month = (r.a >> 8) & 0xff;
+                                                       day = (r.b & 0xff);
+                                                       has_date = true;
+                                               }
+                                       } catch (ParseException pe) {
+                                       }
+                               }
+                               if (has_date && has_flight)
+                                       break;
+                       }
+               }
+       }
+}
diff --git a/altoslib/AltosEepromMega.java b/altoslib/AltosEepromMega.java
new file mode 100644 (file)
index 0000000..26bacf8
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+public class AltosEepromMega {
+       public int      cmd;
+       public int      tick;
+       public boolean  valid;
+       public String   data;
+       public int      a, b;
+
+       public int      data8[];
+
+       public static final int record_length = 32;
+       static final int        header_length = 4;
+       static final int        data_length = record_length - header_length;
+
+       public int data8(int i) {
+               return data8[i];
+       }
+
+       public int data16(int i) {
+               return ((data8[i] | (data8[i+1] << 8)) << 16) >> 16;
+       }
+
+       public int data32(int i) {
+               return data8[i] | (data8[i+1] << 8) | (data8[i+2] << 16) | (data8[i+3] << 24);
+       }
+
+       /* AO_LOG_FLIGHT elements */
+       public int flight() { return data16(0); }
+       public int ground_accel() { return data16(2); }
+       public int ground_pres() { return data32(4); }
+       public int ground_temp() { return data32(8); }
+
+       /* AO_LOG_STATE elements */
+       public int state() { return data16(0); }
+       public int reason() { return data16(2); }
+
+       /* AO_LOG_SENSOR elements */
+       public int pres() { return data32(0); }
+       public int temp() { return data32(4); }
+       public int accel_x() { return data16(8); }
+       public int accel_y() { return data16(10); }
+       public int accel_z() { return data16(12); }
+       public int gyro_x() { return data16(14); }
+       public int gyro_y() { return data16(16); }
+       public int gyro_z() { return data16(18); }
+       public int mag_x() { return data16(20); }
+       public int mag_y() { return data16(22); }
+       public int mag_z() { return data16(24); }
+       public int accel() {
+               int a = data16(26);
+               if (a != 0xffff)
+                       return a;
+               return accel_y();
+       }
+
+       /* AO_LOG_VOLT elements */
+       public int v_batt() { return data16(0); }
+       public int v_pbatt() { return data16(2); }
+       public int nsense() { return data16(4); }
+       public int sense(int i) { return data16(6 + i * 2); }
+
+       public AltosEepromMega (AltosEepromChunk chunk, int start) throws ParseException {
+               cmd = chunk.data(start);
+
+               valid = !chunk.erased(start, record_length);
+               if (valid) {
+                       if (AltosConvert.checksum(chunk.data, start, record_length) != 0)
+                               throw new ParseException(String.format("invalid checksum at 0x%x",
+                                                                      chunk.address + start), 0);
+               } else {
+                       cmd = AltosLib.AO_LOG_INVALID;
+               }
+
+               tick = chunk.data16(start+2);
+
+               data8 = new int[data_length];
+               for (int i = 0; i < data_length; i++)
+                       data8[i] = chunk.data(start + header_length + i);
+       }
+
+       public AltosEepromMega (String line) {
+               valid = false;
+               tick = 0;
+
+               if (line == null) {
+                       cmd = AltosLib.AO_LOG_INVALID;
+                       line = "";
+               } else {
+                       try {
+                               String[] tokens = line.split("\\s+");
+
+                               if (tokens[0].length() == 1) {
+                                       if (tokens.length != 2 + data_length) {
+                                               cmd = AltosLib.AO_LOG_INVALID;
+                                               data = line;
+                                       } else {
+                                               cmd = tokens[0].codePointAt(0);
+                                               tick = Integer.parseInt(tokens[1],16);
+                                               valid = true;
+                                               data8 = new int[data_length];
+                                               for (int i = 0; i < data_length; i++)
+                                                       data8[i] = Integer.parseInt(tokens[2 + i],16);
+                                       }
+                               } else if (tokens[0].equals("Config") && tokens[1].equals("version:")) {
+                                       cmd = AltosLib.AO_LOG_CONFIG_VERSION;
+                                       data = tokens[2];
+                               } else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) {
+                                       cmd = AltosLib.AO_LOG_MAIN_DEPLOY;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) {
+                                       cmd = AltosLib.AO_LOG_APOGEE_DELAY;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) {
+                                       cmd = AltosLib.AO_LOG_RADIO_CHANNEL;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Callsign:")) {
+                                       cmd = AltosLib.AO_LOG_CALLSIGN;
+                                       data = tokens[1].replaceAll("\"","");
+                               } else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) {
+                                       cmd = AltosLib.AO_LOG_ACCEL_CAL;
+                                       a = Integer.parseInt(tokens[3]);
+                                       b = Integer.parseInt(tokens[5]);
+                               } else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) {
+                                       cmd = AltosLib.AO_LOG_RADIO_CAL;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) {
+                                       cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG;
+                                       a = Integer.parseInt(tokens[3]);
+                               } else if (tokens[0].equals("manufacturer")) {
+                                       cmd = AltosLib.AO_LOG_MANUFACTURER;
+                                       data = tokens[1];
+                               } else if (tokens[0].equals("product")) {
+                                       cmd = AltosLib.AO_LOG_PRODUCT;
+                                       data = tokens[1];
+                               } else if (tokens[0].equals("serial-number")) {
+                                       cmd = AltosLib.AO_LOG_SERIAL_NUMBER;
+                                       a = Integer.parseInt(tokens[1]);
+                               } else if (tokens[0].equals("log-format")) {
+                                       cmd = AltosLib.AO_LOG_LOG_FORMAT;
+                                       a = Integer.parseInt(tokens[1]);
+                               } else if (tokens[0].equals("software-version")) {
+                                       cmd = AltosLib.AO_LOG_SOFTWARE_VERSION;
+                                       data = tokens[1];
+                               } else if (tokens[0].equals("ms5607")) {
+                                       if (tokens[1].equals("reserved:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_RESERVED;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else if (tokens[1].equals("sens:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_SENS;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else if (tokens[1].equals("off:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_OFF;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else if (tokens[1].equals("tcs:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_TCS;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else if (tokens[1].equals("tco:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_TCO;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else if (tokens[1].equals("tref:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_TREF;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else if (tokens[1].equals("tempsens:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_TEMPSENS;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else if (tokens[1].equals("crc:")) {
+                                               cmd = AltosLib.AO_LOG_BARO_CRC;
+                                               a = Integer.parseInt(tokens[2]);
+                                       } else {
+                                               cmd = AltosLib.AO_LOG_INVALID;
+                                               data = line;
+                                       }
+                               } else {
+                                       cmd = AltosLib.AO_LOG_INVALID;
+                                       data = line;
+                               }
+                       } catch (NumberFormatException ne) {
+                               cmd = AltosLib.AO_LOG_INVALID;
+                               data = line;
+                       }
+               }
+       }
+
+       public AltosEepromMega(int in_cmd, int in_tick) {
+               cmd = in_cmd;
+               tick = in_tick;
+               valid = true;
+       }
+}
diff --git a/altoslib/AltosEepromMegaIterable.java b/altoslib/AltosEepromMegaIterable.java
new file mode 100644 (file)
index 0000000..1ab2fcc
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+
+public class AltosEepromMegaIterable extends AltosRecordIterable {
+
+       static final int        seen_flight = 1;
+       static final int        seen_sensor = 2;
+       static final int        seen_temp_volt = 4;
+       static final int        seen_deploy = 8;
+       static final int        seen_gps_time = 16;
+       static final int        seen_gps_lat = 32;
+       static final int        seen_gps_lon = 64;
+
+       static final int        seen_basic = seen_flight|seen_sensor;
+
+       boolean                 has_accel;
+       boolean                 has_gps;
+       boolean                 has_ignite;
+
+       AltosEepromMega flight_record;
+       AltosEepromMega gps_date_record;
+
+       TreeSet<AltosOrderedMegaRecord> records;
+
+       AltosMs5607             baro;
+
+       LinkedList<AltosRecord> list;
+
+       class EepromState {
+               int     seen;
+               int     n_pad_samples;
+               double  ground_pres;
+               int     gps_tick;
+               int     boost_tick;
+               int     sensor_tick;
+
+               EepromState() {
+                       seen = 0;
+                       n_pad_samples = 0;
+                       ground_pres = 0.0;
+                       gps_tick = 0;
+               }
+       }
+
+       void update_state(AltosRecordMM state, AltosEepromMega record, EepromState eeprom) {
+               state.tick = record.tick;
+               switch (record.cmd) {
+               case AltosLib.AO_LOG_FLIGHT:
+                       eeprom.seen |= seen_flight;
+                       state.ground_accel = record.ground_accel();
+                       state.flight_accel = record.ground_accel();
+                       state.ground_pres = baro.set(record.ground_pres(), record.ground_temp());
+                       state.flight_pres = state.ground_pres;
+                       state.flight = record.data16(0);
+                       eeprom.boost_tick = record.tick;
+                       break;
+               case AltosLib.AO_LOG_SENSOR:
+                       state.accel = record.accel();
+                       baro.set(record.pres(), record.temp());
+                       state.pres = baro.pa;
+                       state.temp = baro.cc;
+                       state.imu = new AltosIMU();
+                       state.imu.accel_x = record.accel_x();
+                       state.imu.accel_y = record.accel_y();
+                       state.imu.accel_z = record.accel_z();
+                       state.imu.gyro_x = record.gyro_x();
+                       state.imu.gyro_y = record.gyro_y();
+                       state.imu.gyro_z = record.gyro_z();
+                       state.mag = new AltosMag();
+                       state.mag.x = record.mag_x();
+                       state.mag.y = record.mag_y();
+                       state.mag.z = record.mag_z();
+                       if (state.state < AltosLib.ao_flight_boost) {
+                               eeprom.n_pad_samples++;
+                               eeprom.ground_pres += state.pres;
+                               state.ground_pres = (int) (eeprom.ground_pres / eeprom.n_pad_samples);
+                               state.flight_pres = state.ground_pres;
+                       } else {
+                               state.flight_pres = (state.flight_pres * 15 + state.pres) / 16;
+                       }
+                       state.flight_accel = (state.flight_accel * 15 + state.accel) / 16;
+                       if ((eeprom.seen & seen_sensor) == 0)
+                               eeprom.sensor_tick = record.tick - 1;
+                       state.flight_vel += (state.accel_plus_g - state.accel) * (record.tick - eeprom.sensor_tick);
+                       eeprom.seen |= seen_sensor;
+                       eeprom.sensor_tick = record.tick;
+                       has_accel = true;
+                       break;
+               case AltosLib.AO_LOG_PRESSURE:
+                       state.pres = record.b;
+                       state.flight_pres = state.pres;
+                       if (eeprom.n_pad_samples == 0) {
+                               eeprom.n_pad_samples++;
+                               state.ground_pres = state.pres;
+                       }
+                       eeprom.seen |= seen_sensor;
+                       break;
+               case AltosLib.AO_LOG_TEMP_VOLT:
+                       state.v_batt = record.v_batt();
+                       state.v_pyro = record.v_pbatt();
+                       for (int i = 0; i < AltosRecordMM.num_sense; i++)
+                               state.sense[i] = record.sense(i);
+                       eeprom.seen |= seen_temp_volt;
+                       break;
+//
+//             case AltosLib.AO_LOG_DEPLOY:
+//                     state.drogue = record.a;
+//                     state.main = record.b;
+//                     eeprom.seen |= seen_deploy;
+//                     has_ignite = true;
+//                     break;
+
+               case AltosLib.AO_LOG_STATE:
+                       state.state = record.state();
+                       break;
+               case AltosLib.AO_LOG_GPS_TIME:
+                       eeprom.gps_tick = state.tick;
+                       AltosGPS old = state.gps;
+                       state.gps = new AltosGPS();
+
+                       /* GPS date doesn't get repeated through the file */
+                       if (old != null) {
+                               state.gps.year = old.year;
+                               state.gps.month = old.month;
+                               state.gps.day = old.day;
+                       }
+                       state.gps.hour = (record.a & 0xff);
+                       state.gps.minute = (record.a >> 8);
+                       state.gps.second = (record.b & 0xff);
+
+                       int flags = (record.b >> 8);
+                       state.gps.connected = (flags & AltosLib.AO_GPS_RUNNING) != 0;
+                       state.gps.locked = (flags & AltosLib.AO_GPS_VALID) != 0;
+                       state.gps.nsat = (flags & AltosLib.AO_GPS_NUM_SAT_MASK) >>
+                               AltosLib.AO_GPS_NUM_SAT_SHIFT;
+                       state.new_gps = true;
+                       has_gps = true;
+                       break;
+               case AltosLib.AO_LOG_GPS_LAT:
+                       int lat32 = record.a | (record.b << 16);
+                       state.gps.lat = (double) lat32 / 1e7;
+                       break;
+               case AltosLib.AO_LOG_GPS_LON:
+                       int lon32 = record.a | (record.b << 16);
+                       state.gps.lon = (double) lon32 / 1e7;
+                       break;
+               case AltosLib.AO_LOG_GPS_ALT:
+                       state.gps.alt = record.a;
+                       break;
+               case AltosLib.AO_LOG_GPS_SAT:
+                       if (state.tick == eeprom.gps_tick) {
+                               int svid = record.a;
+                               int c_n0 = record.b >> 8;
+                               state.gps.add_sat(svid, c_n0);
+                       }
+                       break;
+               case AltosLib.AO_LOG_GPS_DATE:
+                       state.gps.year = (record.a & 0xff) + 2000;
+                       state.gps.month = record.a >> 8;
+                       state.gps.day = record.b & 0xff;
+                       break;
+
+               case AltosLib.AO_LOG_CONFIG_VERSION:
+                       break;
+               case AltosLib.AO_LOG_MAIN_DEPLOY:
+                       break;
+               case AltosLib.AO_LOG_APOGEE_DELAY:
+                       break;
+               case AltosLib.AO_LOG_RADIO_CHANNEL:
+                       break;
+               case AltosLib.AO_LOG_CALLSIGN:
+                       state.callsign = record.data;
+                       break;
+               case AltosLib.AO_LOG_ACCEL_CAL:
+                       state.accel_plus_g = record.a;
+                       state.accel_minus_g = record.b;
+                       break;
+               case AltosLib.AO_LOG_RADIO_CAL:
+                       break;
+               case AltosLib.AO_LOG_MANUFACTURER:
+                       break;
+               case AltosLib.AO_LOG_PRODUCT:
+                       break;
+               case AltosLib.AO_LOG_SERIAL_NUMBER:
+                       state.serial = record.a;
+                       break;
+               case AltosLib.AO_LOG_SOFTWARE_VERSION:
+                       break;
+               case AltosLib.AO_LOG_BARO_RESERVED:
+                       baro.reserved = record.a;
+                       break;
+               case AltosLib.AO_LOG_BARO_SENS:
+                       baro.sens =record.a;
+                       break;
+               case AltosLib.AO_LOG_BARO_OFF:
+                       baro.off =record.a;
+                       break;
+               case AltosLib.AO_LOG_BARO_TCS:
+                       baro.tcs =record.a;
+                       break;
+               case AltosLib.AO_LOG_BARO_TCO:
+                       baro.tco =record.a;
+                       break;
+               case AltosLib.AO_LOG_BARO_TREF:
+                       baro.tref =record.a;
+                       break;
+               case AltosLib.AO_LOG_BARO_TEMPSENS:
+                       baro.tempsens =record.a;
+                       break;
+               case AltosLib.AO_LOG_BARO_CRC:
+                       baro.crc =record.a;
+                       break;
+               }
+               state.seen |= eeprom.seen;
+       }
+
+       LinkedList<AltosRecord> make_list() {
+               LinkedList<AltosRecord>         list = new LinkedList<AltosRecord>();
+               Iterator<AltosOrderedMegaRecord>        iterator = records.iterator();
+               AltosOrderedMegaRecord          record = null;
+               AltosRecordMM                   state = new AltosRecordMM();
+               //boolean                               last_reported = false;
+               EepromState                     eeprom = new EepromState();
+
+               state.state = AltosLib.ao_flight_pad;
+               state.accel_plus_g = 15758;
+               state.accel_minus_g = 16294;
+
+               /* Pull in static data from the flight and gps_date records */
+               if (flight_record != null)
+                       update_state(state, flight_record, eeprom);
+               if (gps_date_record != null)
+                       update_state(state, gps_date_record, eeprom);
+
+               while (iterator.hasNext()) {
+                       record = iterator.next();
+                       if ((eeprom.seen & seen_basic) == seen_basic && record.tick != state.tick) {
+                               AltosRecordMM r = state.clone();
+                               r.time = (r.tick - eeprom.boost_tick) / 100.0;
+                               list.add(r);
+                       }
+                       update_state(state, record, eeprom);
+               }
+               AltosRecordMM r = state.clone();
+               r.time = (r.tick - eeprom.boost_tick) / 100.0;
+               list.add(r);
+               return list;
+       }
+
+       public Iterator<AltosRecord> iterator() {
+               if (list == null)
+                       list = make_list();
+               return list.iterator();
+       }
+
+       public boolean has_gps() { return has_gps; }
+       public boolean has_accel() { return has_accel; }
+       public boolean has_ignite() { return has_ignite; }
+
+       public void write_comments(PrintStream out) {
+               Iterator<AltosOrderedMegaRecord>        iterator = records.iterator();
+               out.printf("# Comments\n");
+               while (iterator.hasNext()) {
+                       AltosOrderedMegaRecord  record = iterator.next();
+                       switch (record.cmd) {
+                       case AltosLib.AO_LOG_CONFIG_VERSION:
+                               out.printf("# Config version: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_MAIN_DEPLOY:
+                               out.printf("# Main deploy: %s\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_APOGEE_DELAY:
+                               out.printf("# Apogee delay: %s\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_RADIO_CHANNEL:
+                               out.printf("# Radio channel: %s\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_CALLSIGN:
+                               out.printf("# Callsign: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_ACCEL_CAL:
+                               out.printf ("# Accel cal: %d %d\n", record.a, record.b);
+                               break;
+                       case AltosLib.AO_LOG_RADIO_CAL:
+                               out.printf ("# Radio cal: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_MAX_FLIGHT_LOG:
+                               out.printf ("# Max flight log: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_MANUFACTURER:
+                               out.printf ("# Manufacturer: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_PRODUCT:
+                               out.printf ("# Product: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_SERIAL_NUMBER:
+                               out.printf ("# Serial number: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_SOFTWARE_VERSION:
+                               out.printf ("# Software version: %s\n", record.data);
+                               break;
+                       case AltosLib.AO_LOG_BARO_RESERVED:
+                               out.printf ("# Baro reserved: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_SENS:
+                               out.printf ("# Baro sens: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_OFF:
+                               out.printf ("# Baro off: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TCS:
+                               out.printf ("# Baro tcs: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TCO:
+                               out.printf ("# Baro tco: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TREF:
+                               out.printf ("# Baro tref: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_TEMPSENS:
+                               out.printf ("# Baro tempsens: %d\n", record.a);
+                               break;
+                       case AltosLib.AO_LOG_BARO_CRC:
+                               out.printf ("# Baro crc: %d\n", record.a);
+                               break;
+                       }
+               }
+       }
+
+       /*
+        * Given an AO_LOG_GPS_TIME record with correct time, and one
+        * missing time, rewrite the missing time values with the good
+        * ones, assuming that the difference between them is 'diff' seconds
+        */
+       void update_time(AltosOrderedMegaRecord good, AltosOrderedMegaRecord bad) {
+
+               int diff = (bad.tick - good.tick + 50) / 100;
+
+               int hour = (good.a & 0xff);
+               int minute = (good.a >> 8);
+               int second = (good.b & 0xff);
+               int flags = (good.b >> 8);
+               int seconds = hour * 3600 + minute * 60 + second;
+
+               /* Make sure this looks like a good GPS value */
+               if ((flags & AltosLib.AO_GPS_NUM_SAT_MASK) >> AltosLib.AO_GPS_NUM_SAT_SHIFT < 4)
+                       flags = (flags & ~AltosLib.AO_GPS_NUM_SAT_MASK) | (4 << AltosLib.AO_GPS_NUM_SAT_SHIFT);
+               flags |= AltosLib.AO_GPS_RUNNING;
+               flags |= AltosLib.AO_GPS_VALID;
+
+               int new_seconds = seconds + diff;
+               if (new_seconds < 0)
+                       new_seconds += 24 * 3600;
+               int new_second = (new_seconds % 60);
+               int new_minutes = (new_seconds / 60);
+               int new_minute = (new_minutes % 60);
+               int new_hours = (new_minutes / 60);
+               int new_hour = (new_hours % 24);
+
+               bad.a = new_hour + (new_minute << 8);
+               bad.b = new_second + (flags << 8);
+       }
+
+       /*
+        * Read the whole file, dumping records into a RB tree so
+        * we can enumerate them in time order -- the eeprom data
+        * are sometimes out of order with GPS data getting timestamps
+        * matching the first packet out of the GPS unit but not
+        * written until the final GPS packet has been received.
+        */
+       public AltosEepromMegaIterable (FileInputStream input) {
+               records = new TreeSet<AltosOrderedMegaRecord>();
+
+               AltosOrderedMegaRecord last_gps_time = null;
+
+               baro = new AltosMs5607();
+
+               int index = 0;
+               int prev_tick = 0;
+               boolean prev_tick_valid = false;
+               boolean missing_time = false;
+
+               try {
+                       for (;;) {
+                               String line = AltosLib.gets(input);
+                               if (line == null)
+                                       break;
+                               AltosOrderedMegaRecord record = new AltosOrderedMegaRecord(line, index++, prev_tick, prev_tick_valid);
+                               if (record.cmd == AltosLib.AO_LOG_INVALID)
+                                       continue;
+                               prev_tick = record.tick;
+                               if (record.cmd < AltosLib.AO_LOG_CONFIG_VERSION)
+                                       prev_tick_valid = true;
+                               if (record.cmd == AltosLib.AO_LOG_FLIGHT) {
+                                       flight_record = record;
+                                       continue;
+                               }
+
+                               /* Two firmware bugs caused the loss of some GPS data.
+                                * The flight date would never be recorded, and often
+                                * the flight time would get overwritten by another
+                                * record. Detect the loss of the GPS date and fix up the
+                                * missing time records
+                                */
+                               if (record.cmd == AltosLib.AO_LOG_GPS_DATE) {
+                                       gps_date_record = record;
+                                       continue;
+                               }
+
+                               /* go back and fix up any missing time values */
+                               if (record.cmd == AltosLib.AO_LOG_GPS_TIME) {
+                                       last_gps_time = record;
+                                       if (missing_time) {
+                                               Iterator<AltosOrderedMegaRecord> iterator = records.iterator();
+                                               while (iterator.hasNext()) {
+                                                       AltosOrderedMegaRecord old = iterator.next();
+                                                       if (old.cmd == AltosLib.AO_LOG_GPS_TIME &&
+                                                           old.a == -1 && old.b == -1)
+                                                       {
+                                                               update_time(record, old);
+                                                       }
+                                               }
+                                               missing_time = false;
+                                       }
+                               }
+
+                               if (record.cmd == AltosLib.AO_LOG_GPS_LAT) {
+                                       if (last_gps_time == null || last_gps_time.tick != record.tick) {
+                                               AltosOrderedMegaRecord add_gps_time = new AltosOrderedMegaRecord(AltosLib.AO_LOG_GPS_TIME,
+                                                                                                        record.tick,
+                                                                                                        -1, -1, index-1);
+                                               if (last_gps_time != null)
+                                                       update_time(last_gps_time, add_gps_time);
+                                               else
+                                                       missing_time = true;
+
+                                               records.add(add_gps_time);
+                                               record.index = index++;
+                                       }
+                               }
+                               records.add(record);
+
+                               /* Bail after reading the 'landed' record; we're all done */
+                               if (record.cmd == AltosLib.AO_LOG_STATE &&
+                                   record.a == AltosLib.ao_flight_landed)
+                                       break;
+                       }
+               } catch (IOException io) {
+               } catch (ParseException pe) {
+               }
+               try {
+                       input.close();
+               } catch (IOException ie) {
+               }
+       }
+}
diff --git a/altoslib/AltosEepromRecord.java b/altoslib/AltosEepromRecord.java
new file mode 100644 (file)
index 0000000..c7ced6a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+public class AltosEepromRecord {
+       public int      cmd;
+       public int      tick;
+       public int      a;
+       public int      b;
+       public String   data;
+       public boolean  tick_valid;
+
+       public static final int record_length = 8;
+
+       public AltosEepromRecord (AltosEepromChunk chunk, int start) throws ParseException {
+
+               cmd = chunk.data(start);
+               tick_valid = true;
+
+               tick_valid = !chunk.erased(start, record_length);
+               if (tick_valid) {
+                       if (AltosConvert.checksum(chunk.data, start, record_length) != 0)
+                               throw new ParseException(String.format("invalid checksum at 0x%x",
+                                                                      chunk.address + start), 0);
+               } else {
+                       cmd = AltosLib.AO_LOG_INVALID;
+               }
+
+               tick = chunk.data16(start + 2);
+               a = chunk.data16(start + 4);
+               b = chunk.data16(start + 6);
+
+               data = null;
+       }
+
+       public AltosEepromRecord (String line) {
+               tick_valid = false;
+               tick = 0;
+               a = 0;
+               b = 0;
+               data = null;
+               if (line == null) {
+                       cmd = AltosLib.AO_LOG_INVALID;
+                       data = "";
+               } else {
+                       try {
+                               String[] tokens = line.split("\\s+");
+
+                               if (tokens[0].length() == 1) {
+                                       if (tokens.length != 4) {
+                                               cmd = AltosLib.AO_LOG_INVALID;
+                                               data = line;
+                                       } else {
+                                               cmd = tokens[0].codePointAt(0);
+                                               tick = Integer.parseInt(tokens[1],16);
+                                               tick_valid = true;
+                                               a = Integer.parseInt(tokens[2],16);
+                                               b = Integer.parseInt(tokens[3],16);
+                                       }
+                               } else if (tokens[0].equals("Config") && tokens[1].equals("version:")) {
+                                       cmd = AltosLib.AO_LOG_CONFIG_VERSION;
+                                       data = tokens[2];
+                               } else if (tokens[0].equals("Main") && tokens[1].equals("deploy:")) {
+                                       cmd = AltosLib.AO_LOG_MAIN_DEPLOY;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Apogee") && tokens[1].equals("delay:")) {
+                                       cmd = AltosLib.AO_LOG_APOGEE_DELAY;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Radio") && tokens[1].equals("channel:")) {
+                                       cmd = AltosLib.AO_LOG_RADIO_CHANNEL;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Callsign:")) {
+                                       cmd = AltosLib.AO_LOG_CALLSIGN;
+                                       data = tokens[1].replaceAll("\"","");
+                               } else if (tokens[0].equals("Accel") && tokens[1].equals("cal")) {
+                                       cmd = AltosLib.AO_LOG_ACCEL_CAL;
+                                       a = Integer.parseInt(tokens[3]);
+                                       b = Integer.parseInt(tokens[5]);
+                               } else if (tokens[0].equals("Radio") && tokens[1].equals("cal:")) {
+                                       cmd = AltosLib.AO_LOG_RADIO_CAL;
+                                       a = Integer.parseInt(tokens[2]);
+                               } else if (tokens[0].equals("Max") && tokens[1].equals("flight") && tokens[2].equals("log:")) {
+                                       cmd = AltosLib.AO_LOG_MAX_FLIGHT_LOG;
+                                       a = Integer.parseInt(tokens[3]);
+                               } else if (tokens[0].equals("manufacturer")) {
+                                       cmd = AltosLib.AO_LOG_MANUFACTURER;
+                                       data = tokens[1];
+                               } else if (tokens[0].equals("product")) {
+                                       cmd = AltosLib.AO_LOG_PRODUCT;
+                                       data = tokens[1];
+                               } else if (tokens[0].equals("serial-number")) {
+                                       cmd = AltosLib.AO_LOG_SERIAL_NUMBER;
+                                       a = Integer.parseInt(tokens[1]);
+                               } else if (tokens[0].equals("log-format")) {
+                                       cmd = AltosLib.AO_LOG_LOG_FORMAT;
+                                       a = Integer.parseInt(tokens[1]);
+                               } else if (tokens[0].equals("software-version")) {
+                                       cmd = AltosLib.AO_LOG_SOFTWARE_VERSION;
+                                       data = tokens[1];
+                               } else {
+                                       cmd = AltosLib.AO_LOG_INVALID;
+                                       data = line;
+                               }
+                       } catch (NumberFormatException ne) {
+                               cmd = AltosLib.AO_LOG_INVALID;
+                               data = line;
+                       }
+               }
+       }
+
+       public AltosEepromRecord(int in_cmd, int in_tick, int in_a, int in_b) {
+               tick_valid = true;
+               cmd = in_cmd;
+               tick = in_tick;
+               a = in_a;
+               b = in_b;
+       }
+}
diff --git a/altoslib/AltosEepromTeleScience.java b/altoslib/AltosEepromTeleScience.java
new file mode 100644 (file)
index 0000000..02ce455
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+public class AltosEepromTeleScience {
+       public int      type;
+       public int      tick;
+       public int      tm_state;
+       public int      tm_tick;
+       public int[]    data;
+       public boolean  valid;
+
+       public static final int AO_LOG_TELESCIENCE_START = 's';
+       public static final int AO_LOG_TELESCIENCE_DATA = 'd';
+
+       static final int        max_data = 12;
+       public static final int record_length = 32;
+
+       public AltosEepromTeleScience (AltosEepromChunk chunk, int start) throws ParseException {
+               type = chunk.data(start);
+
+               valid = !chunk.erased(start, record_length);
+               if (valid) {
+                       if (AltosConvert.checksum(chunk.data, start, record_length) != 0)
+                               throw new ParseException(String.format("invalid checksum at 0x%x",
+                                                                      chunk.address + start), 0);
+               } else {
+                       type = AltosLib.AO_LOG_INVALID;
+               }
+
+               tick = chunk.data16(start+2);
+               tm_tick = chunk.data16(start+4);
+               tm_state = chunk.data(start+6);
+               data = new int[max_data];
+               for (int i = 0; i < max_data; i++)
+                       data[i] = chunk.data16(start + 8 + i * 2);
+       }
+}
diff --git a/altoslib/AltosFile.java b/altoslib/AltosFile.java
new file mode 100644 (file)
index 0000000..1ab00b3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * 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 org.altusmetrum.AltosLib; 
+
+import java.io.File;
+import java.util.*;
+
+public class AltosFile extends File {
+
+       public AltosFile(int year, int month, int day, int serial, int flight, String extension) {
+               super (AltosPreferences.logdir(),
+                      String.format("%04d-%02d-%02d-serial-%03d-flight-%03d.%s",
+                                    year, month, day, serial, flight, extension));
+       }
+
+       public AltosFile(int serial, int flight, String extension) {
+               this(Calendar.getInstance().get(Calendar.YEAR),
+                    Calendar.getInstance().get(Calendar.MONTH) + 1,
+                    Calendar.getInstance().get(Calendar.DAY_OF_MONTH),
+                    serial,
+                    flight,
+                    extension);
+       }
+
+       public AltosFile(AltosRecord telem) {
+               this(telem.serial, telem.flight, "telem");
+       }
+}
diff --git a/altoslib/AltosFlightReader.java b/altoslib/AltosFlightReader.java
new file mode 100644 (file)
index 0000000..cbd6415
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+import java.io.*;
+import java.util.concurrent.*;
+
+public class AltosFlightReader {
+       public String name;
+
+       public int serial;
+
+       public void init() { }
+
+       public AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException { return null; }
+
+       public void close(boolean interrupted) { }
+
+       public void set_frequency(double frequency) throws InterruptedException, TimeoutException { }
+
+       public void save_frequency() { }
+
+       public void set_telemetry(int telemetry) { }
+
+       public void save_telemetry() { }
+
+       public void update(AltosState state) throws InterruptedException { }
+
+       public boolean supports_telemetry(int telemetry) { return false; }
+
+       public File backing_file() { return null; }
+}
diff --git a/altoslib/AltosFrequency.java b/altoslib/AltosFrequency.java
new file mode 100644 (file)
index 0000000..e20f03b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+public class AltosFrequency {
+       public double   frequency;
+       public String   description;
+
+       public String toString() {
+               return String.format("%7.3f MHz %-20s",
+                                    frequency, description);
+       }
+
+       public String toShortString() {
+               return String.format("%7.3f MHz %s",
+                                    frequency, description);
+       }
+
+       public boolean close(double f) {
+               double  diff = Math.abs(frequency - f);
+
+               return diff < 0.010;
+       }
+
+       public AltosFrequency(double f, String d) {
+               frequency = f;
+               description = d;
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosGPS.java b/altoslib/AltosGPS.java
new file mode 100644 (file)
index 0000000..ea0949e
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+public class AltosGPS {
+
+       public final static int MISSING = AltosRecord.MISSING;
+
+       public int      nsat;
+       public boolean  locked;
+       public boolean  connected;
+       public double   lat;            /* degrees (+N -S) */
+       public double   lon;            /* degrees (+E -W) */
+       public int      alt;            /* m */
+       public int      year;
+       public int      month;
+       public int      day;
+       public int      hour;
+       public int      minute;
+       public int      second;
+
+       public double   ground_speed;   /* m/s */
+       public int      course;         /* degrees */
+       public double   climb_rate;     /* m/s */
+       public double   hdop;           /* unitless */
+       public double   vdop;           /* unitless */
+       public int      h_error;        /* m */
+       public int      v_error;        /* m */
+
+       public AltosGPSSat[] cc_gps_sat;        /* tracking data */
+
+       public void ParseGPSDate(String date) throws ParseException {
+               String[] ymd = date.split("-");
+               if (ymd.length != 3)
+                       throw new ParseException("error parsing GPS date " + date + " got " + ymd.length, 0);
+               year = AltosParse.parse_int(ymd[0]);
+               month = AltosParse.parse_int(ymd[1]);
+               day = AltosParse.parse_int(ymd[2]);
+       }
+
+       public void ParseGPSTime(String time) throws ParseException {
+               String[] hms = time.split(":");
+               if (hms.length != 3)
+                       throw new ParseException("Error parsing GPS time " + time + " got " + hms.length, 0);
+               hour = AltosParse.parse_int(hms[0]);
+               minute = AltosParse.parse_int(hms[1]);
+               second = AltosParse.parse_int(hms[2]);
+       }
+
+       public void ClearGPSTime() {
+               year = month = day = 0;
+               hour = minute = second = 0;
+       }
+
+       public AltosGPS(AltosTelemetryMap map) throws ParseException {
+               String  state = map.get_string(AltosTelemetry.AO_TELEM_GPS_STATE,
+                                              AltosTelemetry.AO_TELEM_GPS_STATE_ERROR);
+
+               nsat = map.get_int(AltosTelemetry.AO_TELEM_GPS_NUM_SAT, 0);
+               if (state.equals(AltosTelemetry.AO_TELEM_GPS_STATE_LOCKED)) {
+                       connected = true;
+                       locked = true;
+                       lat = map.get_double(AltosTelemetry.AO_TELEM_GPS_LATITUDE, MISSING, 1.0e-7);
+                       lon = map.get_double(AltosTelemetry.AO_TELEM_GPS_LONGITUDE, MISSING, 1.0e-7);
+                       alt = map.get_int(AltosTelemetry.AO_TELEM_GPS_ALTITUDE, MISSING);
+                       year = map.get_int(AltosTelemetry.AO_TELEM_GPS_YEAR, MISSING);
+                       if (year != MISSING)
+                               year += 2000;
+                       month = map.get_int(AltosTelemetry.AO_TELEM_GPS_MONTH, MISSING);
+                       day = map.get_int(AltosTelemetry.AO_TELEM_GPS_DAY, MISSING);
+
+                       hour = map.get_int(AltosTelemetry.AO_TELEM_GPS_HOUR, 0);
+                       minute = map.get_int(AltosTelemetry.AO_TELEM_GPS_MINUTE, 0);
+                       second = map.get_int(AltosTelemetry.AO_TELEM_GPS_SECOND, 0);
+
+                       ground_speed = map.get_double(AltosTelemetry.AO_TELEM_GPS_HORIZONTAL_SPEED,
+                                                     AltosRecord.MISSING, 1/100.0);
+                       course = map.get_int(AltosTelemetry.AO_TELEM_GPS_COURSE,
+                                            AltosRecord.MISSING);
+                       hdop = map.get_double(AltosTelemetry.AO_TELEM_GPS_HDOP, MISSING, 1.0);
+                       vdop = map.get_double(AltosTelemetry.AO_TELEM_GPS_VDOP, MISSING, 1.0);
+                       h_error = map.get_int(AltosTelemetry.AO_TELEM_GPS_HERROR, MISSING);
+                       v_error = map.get_int(AltosTelemetry.AO_TELEM_GPS_VERROR, MISSING);
+               } else if (state.equals(AltosTelemetry.AO_TELEM_GPS_STATE_UNLOCKED)) {
+                       connected = true;
+                       locked = false;
+               } else {
+                       connected = false;
+                       locked = false;
+               }
+       }
+
+       public AltosGPS(String[] words, int i, int version) throws ParseException {
+               AltosParse.word(words[i++], "GPS");
+               nsat = AltosParse.parse_int(words[i++]);
+               AltosParse.word(words[i++], "sat");
+
+               connected = false;
+               locked = false;
+               lat = lon = 0;
+               alt = 0;
+               ClearGPSTime();
+               if ((words[i]).equals("unlocked")) {
+                       connected = true;
+                       i++;
+               } else if ((words[i]).equals("not-connected")) {
+                       i++;
+               } else if (words.length >= 40) {
+                       locked = true;
+                       connected = true;
+
+                       if (version > 1)
+                               ParseGPSDate(words[i++]);
+                       else
+                               year = month = day = 0;
+                       ParseGPSTime(words[i++]);
+                       lat = AltosParse.parse_coord(words[i++]);
+                       lon = AltosParse.parse_coord(words[i++]);
+                       alt = AltosParse.parse_int(words[i++]);
+                       if (version > 1 || (i < words.length && !words[i].equals("SAT"))) {
+                               ground_speed = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(H)"));
+                               course = AltosParse.parse_int(words[i++]);
+                               climb_rate = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "m/s(V)"));
+                               hdop = AltosParse.parse_double(AltosParse.strip_suffix(words[i++], "(hdop)"));
+                               h_error = AltosParse.parse_int(words[i++]);
+                               v_error = AltosParse.parse_int(words[i++]);
+                       }
+               } else {
+                       i++;
+               }
+               if (i < words.length) {
+                       AltosParse.word(words[i++], "SAT");
+                       int tracking_channels = 0;
+                       if (words[i].equals("not-connected"))
+                               tracking_channels = 0;
+                       else
+                               tracking_channels = AltosParse.parse_int(words[i]);
+                       i++;
+                       cc_gps_sat = new AltosGPSSat[tracking_channels];
+                       for (int chan = 0; chan < tracking_channels; chan++) {
+                               cc_gps_sat[chan] = new AltosGPSSat();
+                               cc_gps_sat[chan].svid = AltosParse.parse_int(words[i++]);
+                               /* Older versions included SiRF status bits */
+                               if (version < 2)
+                                       i++;
+                               cc_gps_sat[chan].c_n0 = AltosParse.parse_int(words[i++]);
+                       }
+               } else
+                       cc_gps_sat = new AltosGPSSat[0];
+       }
+
+       public void set_latitude(int in_lat) {
+               lat = in_lat / 10.0e7;
+       }
+
+       public void set_longitude(int in_lon) {
+               lon = in_lon / 10.0e7;
+       }
+
+       public void set_time(int in_hour, int in_minute, int in_second) {
+               hour = in_hour;
+               minute = in_minute;
+               second = in_second;
+       }
+
+       public void set_date(int in_year, int in_month, int in_day) {
+               year = in_year;
+               month = in_month;
+               day = in_day;
+       }
+
+       /*
+       public void set_flags(int in_flags) {
+               flags = in_flags;
+       }
+       */
+
+       public void set_altitude(int in_altitude) {
+               alt = in_altitude;
+       }
+
+       public void add_sat(int svid, int c_n0) {
+               if (cc_gps_sat == null) {
+                       cc_gps_sat = new AltosGPSSat[1];
+               } else {
+                       AltosGPSSat[] new_gps_sat = new AltosGPSSat[cc_gps_sat.length + 1];
+                       for (int i = 0; i < cc_gps_sat.length; i++)
+                               new_gps_sat[i] = cc_gps_sat[i];
+                       cc_gps_sat = new_gps_sat;
+               }
+               AltosGPSSat     sat = new AltosGPSSat();
+               sat.svid = svid;
+               sat.c_n0 = c_n0;
+               cc_gps_sat[cc_gps_sat.length - 1] = sat;
+       }
+
+       public AltosGPS() {
+               ClearGPSTime();
+               cc_gps_sat = null;
+       }
+
+       public AltosGPS(AltosGPS old) {
+               nsat = old.nsat;
+               locked = old.locked;
+               connected = old.connected;
+               lat = old.lat;          /* degrees (+N -S) */
+               lon = old.lon;          /* degrees (+E -W) */
+               alt = old.alt;          /* m */
+               year = old.year;
+               month = old.month;
+               day = old.day;
+               hour = old.hour;
+               minute = old.minute;
+               second = old.second;
+
+               ground_speed = old.ground_speed;        /* m/s */
+               course = old.course;            /* degrees */
+               climb_rate = old.climb_rate;    /* m/s */
+               hdop = old.hdop;                /* unitless? */
+               h_error = old.h_error;  /* m */
+               v_error = old.v_error;  /* m */
+
+               if (old.cc_gps_sat != null) {
+                       cc_gps_sat = new AltosGPSSat[old.cc_gps_sat.length];
+                       for (int i = 0; i < old.cc_gps_sat.length; i++) {
+                               cc_gps_sat[i] = new AltosGPSSat();
+                               cc_gps_sat[i].svid = old.cc_gps_sat[i].svid;
+                               cc_gps_sat[i].c_n0 = old.cc_gps_sat[i].c_n0;
+                       }
+               }
+       }
+}
diff --git a/altoslib/AltosGPSQuery.java b/altoslib/AltosGPSQuery.java
new file mode 100644 (file)
index 0000000..e93af25
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+import java.util.concurrent.*;
+
+class AltosGPSQuery extends AltosGPS {
+       public AltosGPSQuery (AltosLink link, AltosConfigData config_data)
+               throws TimeoutException, InterruptedException {
+               boolean says_done = config_data.compare_version("1.0") >= 0;
+               link.printf("g\n");
+               for (;;) {
+                       String line = link.get_reply_no_dialog(5000);
+                       if (line == null)
+                               throw new TimeoutException();
+                       String[] bits = line.split("\\s+");
+                       if (bits.length == 0)
+                               continue;
+                       if (line.startsWith("Date:")) {
+                               if (bits.length < 2)
+                                       continue;
+                               String[] d = bits[1].split(":");
+                               if (d.length < 3)
+                                       continue;
+                               year = Integer.parseInt(d[0]) + 2000;
+                               month = Integer.parseInt(d[1]);
+                               day = Integer.parseInt(d[2]);
+                               continue;
+                       }
+                       if (line.startsWith("Time:")) {
+                               if (bits.length < 2)
+                                       continue;
+                               String[] d = bits[1].split("/");
+                               if (d.length < 3)
+                                       continue;
+                               hour = Integer.parseInt(d[0]);
+                               minute = Integer.parseInt(d[1]);
+                               second = Integer.parseInt(d[2]);
+                               continue;
+                       }
+                       if (line.startsWith("Lat/Lon:")) {
+                               if (bits.length < 3)
+                                       continue;
+                               lat = Integer.parseInt(bits[1]) * 1.0e-7;
+                               lon = Integer.parseInt(bits[2]) * 1.0e-7;
+                               continue;
+                       }
+                       if (line.startsWith("Alt:")) {
+                               if (bits.length < 2)
+                                       continue;
+                               alt = Integer.parseInt(bits[1]);
+                               continue;
+                       }
+                       if (line.startsWith("Flags:")) {
+                               if (bits.length < 2)
+                                       continue;
+                               int status = Integer.decode(bits[1]);
+                               connected = (status & AltosLib.AO_GPS_RUNNING) != 0;
+                               locked = (status & AltosLib.AO_GPS_VALID) != 0;
+                               if (!says_done)
+                                       break;
+                               continue;
+                       }
+                       if (line.startsWith("Sats:")) {
+                               if (bits.length < 2)
+                                       continue;
+                               nsat = Integer.parseInt(bits[1]);
+                               cc_gps_sat = new AltosGPSSat[nsat];
+                               for (int i = 0; i < nsat; i++) {
+                                       int     svid = Integer.parseInt(bits[2+i*2]);
+                                       int     cc_n0 = Integer.parseInt(bits[3+i*2]);
+                                       cc_gps_sat[i] = new AltosGPSSat(svid, cc_n0);
+                               }
+                       }
+                       if (line.startsWith("done"))
+                               break;
+                       if (line.startsWith("Syntax error"))
+                               break;
+               }
+       }
+}
+
diff --git a/altoslib/AltosGPSSat.java b/altoslib/AltosGPSSat.java
new file mode 100644 (file)
index 0000000..faa1ec8
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+public class AltosGPSSat {
+       public int      svid;
+       public int      c_n0;
+
+       public AltosGPSSat(int s, int c) {
+               svid = s;
+               c_n0= c;
+       }
+
+       public AltosGPSSat() {
+       }
+}
+
diff --git a/altoslib/AltosGreatCircle.java b/altoslib/AltosGreatCircle.java
new file mode 100644 (file)
index 0000000..76b7185
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.lang.Math;
+
+public class AltosGreatCircle {
+       public double   distance;
+       public double   bearing;
+
+       double sqr(double a) { return a * a; }
+
+       static final double rad = Math.PI / 180;
+       static final double earth_radius = 6371.2 * 1000;       /* in meters */
+
+       public static final int BEARING_LONG = 0;
+       public static final int BEARING_SHORT = 1;
+       public static final int BEARING_VOICE = 2;
+
+       public 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)
+       {
+               double lat1 = rad * start_lat;
+               double lon1 = rad * -start_lon;
+               double lat2 = rad * end_lat;
+               double lon2 = rad * -end_lon;
+
+               double d_lon = lon2 - lon1;
+
+               /* From http://en.wikipedia.org/wiki/Great-circle_distance */
+               double vdn = Math.sqrt(sqr(Math.cos(lat2) * Math.sin(d_lon)) +
+                                      sqr(Math.cos(lat1) * Math.sin(lat2) -
+                                          Math.sin(lat1) * Math.cos(lat2) * Math.cos(d_lon)));
+               double vdd = Math.sin(lat1) * Math.sin(lat2) + Math.cos(lat1) * Math.cos(lat2) * Math.cos(d_lon);
+               double d = Math.atan2(vdn,vdd);
+               double course;
+
+               if (Math.cos(lat1) < 1e-20) {
+                       if (lat1 > 0)
+                               course = Math.PI;
+                       else
+                               course = -Math.PI;
+               } else {
+                       if (d < 1e-10)
+                               course = 0;
+                       else
+                               course = Math.acos((Math.sin(lat2)-Math.sin(lat1)*Math.cos(d)) /
+                                                  (Math.sin(d)*Math.cos(lat1)));
+                       if (Math.sin(lon2-lon1) > 0)
+                               course = 2 * Math.PI-course;
+               }
+               distance = d * earth_radius;
+               bearing = course * 180/Math.PI;
+       }
+
+       public AltosGreatCircle(AltosGPS start, AltosGPS end) {
+               this(start.lat, start.lon, end.lat, end.lon);
+       }
+
+       public AltosGreatCircle() {
+               distance = 0;
+               bearing = 0;
+       }
+}
diff --git a/altoslib/AltosHeight.java b/altoslib/AltosHeight.java
new file mode 100644 (file)
index 0000000..da7ffda
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosHeight extends AltosUnits {
+
+       public double value(double v) {
+               if (AltosConvert.imperial_units)
+                       return AltosConvert.meters_to_feet(v);
+               return v;
+       }
+
+       public String show_units() {
+               if (AltosConvert.imperial_units)
+                       return "ft";
+               return "m";
+       }
+
+       public String say_units() {
+               if (AltosConvert.imperial_units)
+                       return "feet";
+               return "meters";
+       }
+
+       int show_fraction(int width) {
+               return width / 9;
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosIMU.java b/altoslib/AltosIMU.java
new file mode 100644 (file)
index 0000000..c0eaf13
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosIMU {
+       public int              accel_x;
+       public int              accel_y;
+       public int              accel_z;
+
+       public int              gyro_x;
+       public int              gyro_y;
+       public int              gyro_z;
+}
+       
\ No newline at end of file
diff --git a/altoslib/AltosIMUQuery.java b/altoslib/AltosIMUQuery.java
new file mode 100644 (file)
index 0000000..0965fa3
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+import java.util.concurrent.TimeoutException;
+
+class AltosIMUQuery extends AltosIMU {
+
+       public AltosIMUQuery (AltosLink link) throws InterruptedException, TimeoutException {
+               link.printf("I\n");
+               for (;;) {
+                       String line = link.get_reply_no_dialog(5000);
+                       if (line == null) {
+                               throw new TimeoutException();
+                       }
+                       if (!line.startsWith("Accel:"))
+                               continue;
+                       String[] items = line.split("\\s+");
+                       if (items.length >= 8) {
+                               accel_x = Integer.parseInt(items[1]);
+                               accel_y = Integer.parseInt(items[2]);
+                               accel_z = Integer.parseInt(items[3]);
+                               gyro_x = Integer.parseInt(items[5]);
+                               gyro_y = Integer.parseInt(items[6]);
+                               gyro_z = Integer.parseInt(items[7]);
+                       }
+                       break;
+               }
+       }
+}
+
diff --git a/altoslib/AltosIdleMonitor.java b/altoslib/AltosIdleMonitor.java
new file mode 100644 (file)
index 0000000..ae3b7b0
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.concurrent.*;
+
+
+public class AltosIdleMonitor extends Thread {
+       AltosLink               link;
+       AltosIdleMonitorListener        listener;
+       AltosState              state;
+       boolean                 remote;
+       double                  frequency;
+       AltosState              previous_state;
+       AltosConfigData         config_data;
+       AltosGPS                gps;
+
+       int AltosRSSI() throws TimeoutException, InterruptedException {
+               link.printf("s\n");
+               String line = link.get_reply_no_dialog(5000);
+               if (line == null)
+                       throw new TimeoutException();
+               String[] items = line.split("\\s+");
+               if (items.length < 2)
+                       return 0;
+               if (!items[0].equals("RSSI:"))
+                       return 0;
+               int rssi = Integer.parseInt(items[1]);
+               return rssi;
+       }
+
+       void update_state() throws InterruptedException, TimeoutException {
+               AltosRecord     record = null;
+
+               try {
+                       if (remote) {
+                               link.set_radio_frequency(frequency);
+                               link.start_remote();
+                       } else
+                               link.flush_input();
+                       config_data = new AltosConfigData(link);
+                       if (config_data.product.startsWith("TeleMetrum")) {
+                               record = new AltosSensorTM(link, config_data);
+                       } else if (config_data.product.startsWith("MegaMetrum")) {
+                               AltosRecordMM record_mm = new AltosRecordMM();
+                               AltosSensorMM sensor = new AltosSensorMM(link);
+                               AltosMs5607 ms5607 = new AltosMs5607Query(link);
+                               AltosIMU imu = new AltosIMUQuery(link);
+
+                               record_mm.accel_plus_g = config_data.accel_cal_plus;
+                               record_mm.accel_minus_g = config_data.accel_cal_minus;
+
+                               record_mm.ground_accel = sensor.accel;
+                               record_mm.accel = sensor.accel;
+                               record_mm.ground_pres = ms5607.pa;
+                               record_mm.pres = ms5607.pa;
+                               record_mm.temp = ms5607.cc;
+
+                               record_mm.v_batt = sensor.v_batt;
+                               record_mm.v_pyro = sensor.v_pyro;
+                               record_mm.sense = sensor.sense;
+
+                               record_mm.imu = imu;
+
+                               record = record_mm;
+                       } else
+                               record = new AltosRecord();
+
+                       gps = new AltosGPSQuery(link, config_data);
+
+                       record.version = 0;
+                       record.callsign = config_data.callsign;
+                       record.serial = config_data.serial;
+                       record.flight = config_data.log_available() > 0 ? 255 : 0;
+                       record.status = 0;
+                       record.state = AltosLib.ao_flight_idle;
+                       record.gps = gps;
+                       record.new_gps = true;
+                       state = new AltosState (record, state);
+               } finally {
+                       if (remote) {
+                               link.stop_remote();
+                               if (record != null)
+                                       record.rssi = AltosRSSI();
+                       } else {
+                               if (record != null)
+                                       record.rssi = 0;
+                       }
+               }
+
+       }
+
+       public void set_frequency(double in_frequency) {
+               frequency = in_frequency;
+       }
+
+       public void post_state() {
+               listener.update(state);
+       }
+
+       public void run() {
+               try {
+                       for (;;) {
+                               try {
+                                       update_state();
+                                       post_state();
+                               } catch (TimeoutException te) {
+                               }
+                               Thread.sleep(1000);
+                       }
+               } catch (InterruptedException ie) {
+                       link.close();
+               }
+       }
+
+       public AltosIdleMonitor(AltosIdleMonitorListener in_listener, AltosLink in_link, boolean in_remote)
+               throws FileNotFoundException, InterruptedException, TimeoutException {
+               listener = in_listener;
+               link = in_link;
+               remote = in_remote;
+               state = null;
+       }
+}
diff --git a/altoslib/AltosIdleMonitorListener.java b/altoslib/AltosIdleMonitorListener.java
new file mode 100644 (file)
index 0000000..9f9abab
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public interface AltosIdleMonitorListener {
+       public void update(AltosState state);
+}
\ No newline at end of file
diff --git a/altoslib/AltosIgnite.java b/altoslib/AltosIgnite.java
new file mode 100644 (file)
index 0000000..a48d0b6
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.concurrent.*;
+
+public class AltosIgnite {
+       AltosLink       link;
+       boolean         remote;
+       boolean         link_started;
+
+       public final static int None = 0;
+       public final static int Apogee = 1;
+       public final static int Main = 2;
+
+       public final static int Unknown = 0;
+       public final static int Ready = 1;
+       public final static int Active = 2;
+       public final static int Open = 3;
+
+       private void start_link() throws InterruptedException, TimeoutException {
+               link_started = true;
+               if (remote)
+                       link.start_remote();
+       }
+
+       private void stop_link() throws InterruptedException {
+               if (!link_started)
+                       return;
+               link_started = false;
+               if (link == null)
+                       return;
+               if (remote)
+                       link.stop_remote();
+       }
+
+       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 (link == null)
+                       return status;
+               //string_ref status_name = new string_ref();
+               try {
+                       start_link();
+                       link.printf("t\n");
+                       for (;;) {
+                               String line = link.get_reply(5000);
+                               if (line == null)
+                                       throw new TimeoutException();
+                               String[] items = line.split("\\s+");
+
+                               if (items.length < 4)
+                                       continue;
+
+                               if (!items[0].equals("Igniter:"))
+                                       continue;
+
+                               if (!items[2].equals("Status:"))
+                                       continue;
+
+                               if (items[1].equals("drogue")) {
+                                       if (igniter == Apogee)
+                                               status = status(items[3]);
+                               } else if (items[1].equals("main")) {
+                                       if (igniter == Main)
+                                               status = status(items[3]);
+                                       break;
+                               }
+                       }
+               } finally {
+                       stop_link();
+               }
+               return status;
+       }
+
+       public static 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 (link == null)
+                       return;
+               try {
+                       start_link();
+                       switch (igniter) {
+                       case Main:
+                               link.printf("i DoIt main\n");
+                               break;
+                       case Apogee:
+                               link.printf("i DoIt drogue\n");
+                               break;
+                       }
+               } catch (InterruptedException ie) {
+               } catch (TimeoutException te) {
+               } finally {
+                       try {
+                               stop_link();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       public void close() {
+               try {
+                       stop_link();
+               } catch (InterruptedException ie) {
+               }
+               link.close();
+               link = null;
+       }
+
+       public AltosIgnite(AltosLink in_link, boolean in_remote)
+               throws FileNotFoundException, TimeoutException, InterruptedException {
+
+               link = in_link;
+               remote = in_remote;
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosLib.java b/altoslib/AltosLib.java
new file mode 100644 (file)
index 0000000..192c445
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.util.*;
+import java.io.*;
+import java.nio.charset.Charset;
+
+public class AltosLib {
+       /* EEProm command letters */
+       public static final int AO_LOG_FLIGHT = 'F';
+       public static final int AO_LOG_SENSOR = 'A';
+       public static final int AO_LOG_TEMP_VOLT = 'T';
+       public static final int AO_LOG_DEPLOY = 'D';
+       public static final int AO_LOG_STATE = 'S';
+       public static final int AO_LOG_GPS_TIME = 'G';
+       public static final int AO_LOG_GPS_LAT = 'N';
+       public static final int AO_LOG_GPS_LON = 'W';
+       public static final int AO_LOG_GPS_ALT = 'H';
+       public static final int AO_LOG_GPS_SAT = 'V';
+       public static final int AO_LOG_GPS_DATE = 'Y';
+       public static final int AO_LOG_PRESSURE = 'P';
+
+       /* Added for header fields in eeprom files */
+       public static final int AO_LOG_CONFIG_VERSION = 1000;
+       public static final int AO_LOG_MAIN_DEPLOY = 1001;
+       public static final int AO_LOG_APOGEE_DELAY = 1002;
+       public static final int AO_LOG_RADIO_CHANNEL = 1003;
+       public static final int AO_LOG_CALLSIGN = 1004;
+       public static final int AO_LOG_ACCEL_CAL = 1005;
+       public static final int AO_LOG_RADIO_CAL = 1006;
+       public static final int AO_LOG_MAX_FLIGHT_LOG = 1007;
+       public static final int AO_LOG_MANUFACTURER = 2000;
+       public static final int AO_LOG_PRODUCT = 2001;
+       public static final int AO_LOG_SERIAL_NUMBER = 2002;
+       public static final int AO_LOG_LOG_FORMAT = 2003;
+
+       /* Added for header fields in megametrum files */
+       public static final int AO_LOG_BARO_RESERVED = 3000;
+       public static final int AO_LOG_BARO_SENS = 3001;
+       public static final int AO_LOG_BARO_OFF = 3002;
+       public static final int AO_LOG_BARO_TCS = 3004;
+       public static final int AO_LOG_BARO_TCO = 3005;
+       public static final int AO_LOG_BARO_TREF = 3006;
+       public static final int AO_LOG_BARO_TEMPSENS = 3007;
+       public static final int AO_LOG_BARO_CRC = 3008;
+
+       public static final int AO_LOG_SOFTWARE_VERSION = 9999;
+
+       /* Added to flag invalid records */
+       public static final int AO_LOG_INVALID = -1;
+
+       /* Flight state numbers and names */
+       public static final int ao_flight_startup = 0;
+       public static final int ao_flight_idle = 1;
+       public static final int ao_flight_pad = 2;
+       public static final int ao_flight_boost = 3;
+       public static final int ao_flight_fast = 4;
+       public static final int ao_flight_coast = 5;
+       public static final int ao_flight_drogue = 6;
+       public static final int ao_flight_main = 7;
+       public static final int ao_flight_landed = 8;
+       public static final int ao_flight_invalid = 9;
+
+       /* USB product IDs */
+       public final static int vendor_altusmetrum = 0xfffe;
+
+       public final static int product_altusmetrum = 0x000a;
+       public final static int product_telemetrum = 0x000b;
+       public final static int product_teledongle = 0x000c;
+       public final static int product_teleterra = 0x000d;
+       public final static int product_telebt = 0x000e;
+       public final static int product_telelaunch = 0x000f;
+       public final static int product_telelco = 0x0010;
+       public final static int product_telescience = 0x0011;
+       public final static int product_telepyro =0x0012;
+       public final static int product_megametrum = 0x0023;
+       public final static int product_megadongle = 0x0024;
+       public final static int product_altusmetrum_min = 0x000a;
+       public final static int product_altusmetrum_max = 0x0024;
+
+       public final static int product_any = 0x10000;
+       public final static int product_basestation = 0x10000 + 1;
+       public final static int product_altimeter = 0x10000 + 2;
+
+       /* Bluetooth "identifier" (bluetooth sucks) */
+       public final static String bt_product_telebt = "TeleBT";
+
+       /* Telemetry modes */
+       public static final int ao_telemetry_off = 0;
+       public static final int ao_telemetry_min = 1;
+       public static final int ao_telemetry_standard = 1;
+       public static final int ao_telemetry_0_9 = 2;
+       public static final int ao_telemetry_0_8 = 3;
+       public static final int ao_telemetry_max = 3;
+
+       private static final String[] ao_telemetry_name = {
+               "Off", "Standard Telemetry", "TeleMetrum v0.9", "TeleMetrum v0.8"
+       };
+
+       public static final String launch_sites_url = "http://www.altusmetrum.org/AltOS/launch-sites.txt";
+
+       public static final int ao_telemetry_standard_len = 32;
+       public static final int ao_telemetry_0_9_len = 95;
+       public static final int ao_telemetry_0_8_len = 94;
+
+       private static final int[] ao_telemetry_len = {
+               0, 32, 95, 94
+       };
+
+       private static HashMap<String,Integer>  string_to_state = new HashMap<String,Integer>();
+
+       private static boolean map_initialized = false;
+
+       public static void initialize_map()
+       {
+               string_to_state.put("startup", ao_flight_startup);
+               string_to_state.put("idle", ao_flight_idle);
+               string_to_state.put("pad", ao_flight_pad);
+               string_to_state.put("boost", ao_flight_boost);
+               string_to_state.put("fast", ao_flight_fast);
+               string_to_state.put("coast", ao_flight_coast);
+               string_to_state.put("drogue", ao_flight_drogue);
+               string_to_state.put("apogee", ao_flight_coast);
+               string_to_state.put("main", ao_flight_main);
+               string_to_state.put("landed", ao_flight_landed);
+               string_to_state.put("invalid", ao_flight_invalid);
+               map_initialized = true;
+       }
+
+       public static int telemetry_len(int telemetry) {
+               if (telemetry <= ao_telemetry_max)
+                       return ao_telemetry_len[telemetry];
+               throw new IllegalArgumentException(String.format("Invalid telemetry %d",
+                                                                telemetry));
+       }
+
+       public static String telemetry_name(int telemetry) {
+               if (telemetry <= ao_telemetry_max)
+                       return ao_telemetry_name[telemetry];
+               throw new IllegalArgumentException(String.format("Invalid telemetry %d",
+                                                                telemetry));
+       }
+       
+       private static String[] state_to_string = {
+               "startup",
+               "idle",
+               "pad",
+               "boost",
+               "fast",
+               "coast",
+               "drogue",
+               "main",
+               "landed",
+               "invalid",
+       };
+
+       private static String[] state_to_string_capital = {
+               "Startup",
+               "Idle",
+               "Pad",
+               "Boost",
+               "Fast",
+               "Coast",
+               "Drogue",
+               "Main",
+               "Landed",
+               "Invalid",
+       };
+
+       public static int state(String state) {
+               if (!map_initialized)
+                       initialize_map();
+               if (string_to_state.containsKey(state))
+                       return string_to_state.get(state);
+               return ao_flight_invalid;
+       }
+
+       public static String state_name(int state) {
+               if (state < 0 || state_to_string.length <= state)
+                       return "invalid";
+               return state_to_string[state];
+       }
+
+       public static String state_name_capital(int state) {
+               if (state < 0 || state_to_string.length <= state)
+                       return "invalid";
+               return state_to_string_capital[state];
+       }
+
+       public static final int AO_GPS_VALID = (1 << 4);
+       public static final int AO_GPS_RUNNING = (1 << 5);
+       public static final int AO_GPS_DATE_VALID = (1 << 6);
+       public static final int AO_GPS_NUM_SAT_SHIFT = 0;
+       public static final int AO_GPS_NUM_SAT_MASK = 0xf;
+
+       public static final int AO_LOG_FORMAT_UNKNOWN = 0;
+       public static final int AO_LOG_FORMAT_FULL = 1;
+       public static final int AO_LOG_FORMAT_TINY = 2;
+       public static final int AO_LOG_FORMAT_TELEMETRY = 3;
+       public static final int AO_LOG_FORMAT_TELESCIENCE = 4;
+       public static final int AO_LOG_FORMAT_MEGAMETRUM = 5;
+       public static final int AO_LOG_FORMAT_NONE = 127;
+
+       public static boolean isspace(int c) {
+               switch (c) {
+               case ' ':
+               case '\t':
+                       return true;
+               }
+               return false;
+       }
+
+       public static boolean ishex(int c) {
+               if ('0' <= c && c <= '9')
+                       return true;
+               if ('a' <= c && c <= 'f')
+                       return true;
+               if ('A' <= c && c <= 'F')
+                       return true;
+               return false;
+       }
+
+       public static boolean ishex(String s) {
+               for (int i = 0; i < s.length(); i++)
+                       if (!ishex(s.charAt(i)))
+                               return false;
+               return true;
+       }
+
+       public static int fromhex(int c) {
+               if ('0' <= c && c <= '9')
+                       return c - '0';
+               if ('a' <= c && c <= 'f')
+                       return c - 'a' + 10;
+               if ('A' <= c && c <= 'F')
+                       return c - 'A' + 10;
+               return -1;
+       }
+
+       public static int fromhex(String s) throws NumberFormatException {
+               int c, v = 0;
+               for (int i = 0; i < s.length(); i++) {
+                       c = s.charAt(i);
+                       if (!ishex(c)) {
+                               if (i == 0)
+                                       throw new NumberFormatException(String.format("invalid hex \"%s\"", s));
+                               return v;
+                       }
+                       v = v * 16 + fromhex(c);
+               }
+               return v;
+       }
+
+       public static boolean isdec(int c) {
+               if ('0' <= c && c <= '9')
+                       return true;
+               return false;
+       }
+
+       public static boolean isdec(String s) {
+               for (int i = 0; i < s.length(); i++)
+                       if (!isdec(s.charAt(i)))
+                               return false;
+               return true;
+       }
+
+       public static int fromdec(int c) {
+               if ('0' <= c && c <= '9')
+                       return c - '0';
+               return -1;
+       }
+
+       public static int int8(int[] bytes, int i) {
+               return (int) (byte) bytes[i];
+       }
+
+       public static int uint8(int[] bytes, int i) {
+               return bytes[i];
+       }
+
+       public static int int16(int[] bytes, int i) {
+               return (int) (short) (bytes[i] + (bytes[i+1] << 8));
+       }
+
+       public static int uint16(int[] bytes, int i) {
+               return bytes[i] + (bytes[i+1] << 8);
+       }
+
+       public static int uint32(int[] bytes, int i) {
+               return bytes[i] +
+                       (bytes[i+1] << 8) +
+                       (bytes[i+2] << 16) +
+                       (bytes[i+3] << 24);
+       }
+
+       public static int int32(int[] bytes, int i) {
+               return (int) uint32(bytes, i);
+       }
+
+       public static final Charset     unicode_set = Charset.forName("UTF-8");
+
+       public static String string(int[] bytes, int s, int l) {
+               if (s + l > bytes.length) {
+                       if (s > bytes.length) {
+                               s = bytes.length;
+                               l = 0;
+                       } else {
+                               l = bytes.length - s;
+                       }
+               }
+
+               int i;
+               for (i = l - 1; i >= 0; i--)
+                       if (bytes[s+i] != 0)
+                               break;
+
+               l = i + 1;
+               byte[]  b = new byte[l];
+
+               for (i = 0; i < l; i++)
+                       b[i] = (byte) bytes[s+i];
+               String n = new String(b, unicode_set);
+               return n;
+       }
+
+       public static int hexbyte(String s, int i) {
+               int c0, c1;
+
+               if (s.length() < i + 2)
+                       throw new NumberFormatException(String.format("invalid hex \"%s\"", s));
+               c0 = s.charAt(i);
+               if (!ishex(c0))
+                       throw new NumberFormatException(String.format("invalid hex \"%c\"", c0));
+               c1 = s.charAt(i+1);
+               if (!ishex(c1))
+                       throw new NumberFormatException(String.format("invalid hex \"%c\"", c1));
+               return fromhex(c0) * 16 + fromhex(c1);
+       }
+
+       public static int[] hexbytes(String s) {
+               int     n;
+               int[]   r;
+               int     i;
+
+               if ((s.length() & 1) != 0)
+                       throw new NumberFormatException(String.format("invalid line \"%s\"", s));
+               n = s.length() / 2;
+               r = new int[n];
+               for (i = 0; i < n; i++)
+                       r[i] = hexbyte(s, i * 2);
+               return r;
+       }
+
+       public static int fromdec(String s) throws NumberFormatException {
+               int c, v = 0;
+               int sign = 1;
+               for (int i = 0; i < s.length(); i++) {
+                       c = s.charAt(i);
+                       if (i == 0 && c == '-') {
+                               sign = -1;
+                       } else if (!isdec(c)) {
+                               if (i == 0)
+                                       throw new NumberFormatException(String.format("invalid number \"%s\"", s));
+                               return v;
+                       } else
+                               v = v * 10 + fromdec(c);
+               }
+               return v * sign;
+       }
+
+       public static String gets(FileInputStream s) throws IOException {
+               int c;
+               String  line = "";
+
+               while ((c = s.read()) != -1) {
+                       if (c == '\r')
+                               continue;
+                       if (c == '\n') {
+                               return line;
+                       }
+                       line = line + (char) c;
+               }
+               return null;
+       }
+
+       public static String replace_extension(String input, String extension) {
+               int dot = input.lastIndexOf(".");
+               if (dot > 0)
+                       input = input.substring(0,dot);
+               return input.concat(extension);
+       }
+}
diff --git a/altoslib/AltosLine.java b/altoslib/AltosLine.java
new file mode 100644 (file)
index 0000000..5627795
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+public class AltosLine {
+       public String   line;
+
+       public AltosLine() {
+               line = null;
+       }
+
+       public AltosLine(String s) {
+               line = s;
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosLink.java b/altoslib/AltosLink.java
new file mode 100644 (file)
index 0000000..6d51056
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.concurrent.*;
+import java.util.*;
+
+public abstract class AltosLink implements Runnable {
+
+       public final static int ERROR = -1;
+       public final static int TIMEOUT = -2;
+
+       public abstract int getchar();
+       public abstract void print(String data);
+       public abstract void close();
+
+       public static boolean debug = false;
+       public static void set_debug(boolean in_debug) { debug = in_debug; }
+       LinkedList<String> pending_output = new LinkedList<String>();
+
+       public LinkedList<LinkedBlockingQueue<AltosLine>> monitors = new LinkedList<LinkedBlockingQueue<AltosLine>> ();;
+       public LinkedBlockingQueue<AltosLine> reply_queue = new LinkedBlockingQueue<AltosLine>();
+
+       public void add_monitor(LinkedBlockingQueue<AltosLine> q) {
+               set_monitor(true);
+               monitors.add(q);
+       }
+
+       public void remove_monitor(LinkedBlockingQueue<AltosLine> q) {
+               monitors.remove(q);
+               if (monitors.isEmpty())
+                       set_monitor(false);
+       }
+
+       public void printf(String format, Object ... arguments) {
+               String  line = String.format(format, arguments);
+               if (debug)
+                       pending_output.add(line);
+               print(line);
+       }
+
+       public String get_reply_no_dialog(int timeout) throws InterruptedException, TimeoutException {
+               flush_output();
+               AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
+               if (line != null)
+                       return line.line;
+               return null;
+       }
+
+       public String get_reply() throws InterruptedException {
+               return get_reply(5000);
+       }
+
+               
+       public abstract boolean can_cancel_reply();
+       public abstract boolean show_reply_timeout();
+       public abstract void hide_reply_timeout();
+
+       public boolean  reply_abort;
+       public int      in_reply;
+
+       boolean         reply_timeout_shown = false;
+
+       private boolean check_reply_timeout() {
+               if (!reply_timeout_shown)
+                       reply_timeout_shown = show_reply_timeout();
+               return reply_abort;
+       }
+
+       private void cleanup_reply_timeout() {
+               if (reply_timeout_shown) {
+                       reply_timeout_shown = false;
+                       hide_reply_timeout();
+               }
+       }
+
+
+       public void run () {
+               int c;
+               byte[] line_bytes = null;
+               int line_count = 0;
+
+               try {
+                       for (;;) {
+                               c = getchar();
+                               if (Thread.interrupted()) {
+                                       if (debug)
+                                               System.out.printf("INTERRUPTED\n");
+                                       break;
+                               }
+                               if (c == ERROR) {
+                                       if (debug)
+                                               System.out.printf("ERROR\n");
+                                       add_telem (new AltosLine());
+                                       add_reply (new AltosLine());
+                                       break;
+                               }
+                               if (c == TIMEOUT) {
+                                       if (debug)
+                                               System.out.printf("TIMEOUT\n");
+                                       continue;
+                               }
+                               if (c == '\r')
+                                       continue;
+                               synchronized(this) {
+                                       if (c == '\n') {
+                                               if (line_count != 0) {
+                                                       add_bytes(line_bytes, line_count);
+                                                       line_count = 0;
+                                               }
+                                       } else {
+                                               if (line_bytes == null) {
+                                                       line_bytes = new byte[256];
+                                               } else if (line_count == line_bytes.length) {
+                                                       byte[] new_line_bytes = new byte[line_count * 2];
+                                                       System.arraycopy(line_bytes, 0, new_line_bytes, 0, line_count);
+                                                       line_bytes = new_line_bytes;
+                                               }
+                                               line_bytes[line_count] = (byte) c;
+                                               line_count++;
+                                       }
+                               }
+                       }
+               } catch (InterruptedException e) {
+               }
+       }
+
+       public String get_reply(int timeout) throws InterruptedException {
+               boolean can_cancel = can_cancel_reply();
+               String  reply = null;
+
+               if (!can_cancel && remote)
+                       System.out.printf("Uh-oh, reading remote serial device from swing thread\n");
+
+               if (remote && can_cancel)
+                       timeout = 500;
+               try {
+                       ++in_reply;
+
+                       flush_output();
+
+                       reply_abort = false;
+                       reply_timeout_shown = false;
+                       for (;;) {
+                               AltosLine line = reply_queue.poll(timeout, TimeUnit.MILLISECONDS);
+                               if (line != null) {
+                                       cleanup_reply_timeout();
+                                       reply = line.line;
+                                       break;
+                               }
+                               if (!remote || !can_cancel || check_reply_timeout()) {
+                                       reply = null;
+                                       break;
+                               }
+                       }
+               } finally {
+                       --in_reply;
+               }
+               return reply;
+       }
+
+       public void add_telem(AltosLine line) throws InterruptedException {
+               for (int e = 0; e < monitors.size(); e++) {
+                       LinkedBlockingQueue<AltosLine> q = monitors.get(e);
+                       q.put(line);
+               }
+       }
+
+       public void add_reply(AltosLine line) throws InterruptedException {
+               reply_queue.put (line);
+       }
+
+       public void abort_reply() {
+               try {
+                       add_telem (new AltosLine());
+                       add_reply (new AltosLine());
+               } catch (InterruptedException e) {
+               }
+       }
+
+       public void add_string(String line) throws InterruptedException {
+               if (line.startsWith("TELEM") || line.startsWith("VERSION") || line.startsWith("CRC")) {
+                       add_telem(new AltosLine(line));
+               } else {
+                       add_reply(new AltosLine(line));
+               }
+       }
+
+       public void add_bytes(byte[] bytes, int len) throws InterruptedException {
+               String  line;
+               try {
+                       line = new String(bytes, 0, len, "UTF-8");
+               } catch (UnsupportedEncodingException ue) {
+                       line = "";
+                       for (int i = 0; i < len; i++)
+                               line = line + bytes[i];
+               }
+               if (debug)
+                       System.out.printf("\t\t\t\t\t%s\n", line);
+               add_string(line);
+       }
+
+       public void flush_output() {
+               for (String s : pending_output)
+                       System.out.print(s);
+               pending_output.clear();
+       }
+
+       public void flush_input(int timeout) throws InterruptedException {
+               flush_output();
+               boolean got_some;
+
+               do {
+                       Thread.sleep(timeout);
+                       got_some = !reply_queue.isEmpty();
+                       reply_queue.clear();
+               } while (got_some);
+       }
+
+
+       public void flush_input() throws InterruptedException {
+               if (remote)
+                       flush_input(500);
+               else
+                       flush_input(100);
+       }
+
+
+       /*
+        * Various command-level operations on
+        * the link
+        */
+       public boolean monitor_mode = false;
+       public int telemetry = AltosLib.ao_telemetry_standard;
+       public double frequency;
+       AltosConfigData config_data;
+
+       private int telemetry_len() {
+               return AltosLib.telemetry_len(telemetry);
+       }
+
+       private void set_radio_freq(int frequency) {
+               if (monitor_mode)
+                       printf("m 0\nc F %d\nm %x\n",
+                              frequency, telemetry_len());
+               else
+                       printf("c F %d\n", frequency);
+               flush_output();
+       }
+
+       public void set_radio_frequency(double frequency,
+                                       boolean has_frequency,
+                                       boolean has_setting,
+                                       int cal) {
+               if (debug)
+                       System.out.printf("set_radio_frequency %7.3f (freq %b) (set %b) %d\n", frequency, has_frequency, has_setting, cal);
+               if (frequency == 0)
+                       return;
+               if (has_frequency)
+                       set_radio_freq((int) Math.floor (frequency * 1000));
+               else if (has_setting)
+                       set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal));
+               else
+                       set_channel(AltosConvert.radio_frequency_to_channel(frequency));
+       }
+
+       public void set_radio_frequency(double in_frequency) throws InterruptedException, TimeoutException {
+               frequency = in_frequency;
+               config_data();
+               set_radio_frequency(frequency,
+                                   config_data.radio_frequency != 0,
+                                   config_data.radio_setting != 0,
+                                   config_data.radio_calibration);
+       }
+
+       public void set_telemetry(int in_telemetry) {
+               telemetry = in_telemetry;
+               if (monitor_mode)
+                       printf("m 0\nm %x\n", telemetry_len());
+               flush_output();
+       }
+
+       public void set_monitor(boolean monitor) {
+               monitor_mode = monitor;
+               if (monitor)
+                       printf("m %x\n", telemetry_len());
+               else
+                       printf("m 0\n");
+               flush_output();
+       }
+
+       private void set_channel(int channel) {
+               if (monitor_mode)
+                       printf("m 0\nc r %d\nm %x\n",
+                              channel, telemetry_len());
+               else
+                       printf("c r %d\n", channel);
+               flush_output();
+       }
+
+       private void set_radio_setting(int setting) {
+               if (monitor_mode)
+                       printf("m 0\nc R %d\nm %x\n",
+                              setting, telemetry_len());
+               else
+                       printf("c R %d\n", setting);
+               flush_output();
+       }
+
+       public AltosConfigData config_data() throws InterruptedException, TimeoutException {
+               if (config_data == null)
+                       config_data = new AltosConfigData(this);
+               return config_data;
+       }
+
+       public void set_callsign(String callsign) {
+               printf ("c c %s\n", callsign);
+               flush_output();
+       }
+
+       public boolean remote;
+       public int serial;
+       public String name;
+
+       public void start_remote() throws TimeoutException, InterruptedException {
+               if (debug)
+                       System.out.printf("start remote %7.3f\n", frequency);
+               if (frequency == 0.0)
+                       frequency = AltosPreferences.frequency(serial);
+               set_radio_frequency(frequency);
+               set_callsign(AltosPreferences.callsign());
+               printf("p\nE 0\n");
+               flush_input();
+               remote = true;
+       }
+
+       public void stop_remote() throws InterruptedException {
+               if (debug)
+                       System.out.printf("stop remote\n");
+               try {
+                       flush_input();
+               } finally {
+                       printf ("~\n");
+                       flush_output();
+               }
+               remote = false;
+       }
+
+       public AltosLink() {
+       }
+}
diff --git a/altoslib/AltosLog.java b/altoslib/AltosLog.java
new file mode 100644 (file)
index 0000000..3c12470
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.text.ParseException;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/*
+ * This creates a thread to capture telemetry data and write it to
+ * a log file
+ */
+class AltosLog implements Runnable {
+
+       LinkedBlockingQueue<AltosLine>  input_queue;
+       LinkedBlockingQueue<String>     pending_queue;
+       int                             serial;
+       int                             flight;
+       FileWriter                      log_file;
+       Thread                          log_thread;
+       AltosFile                       file;
+
+       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;
+               }
+       }
+
+       File file() {
+               return file;
+       }
+
+       boolean open (AltosRecord telem) throws IOException {
+               AltosFile       a = new AltosFile(telem);
+
+               log_file = new FileWriter(a, true);
+               if (log_file != null) {
+                       while (!pending_queue.isEmpty()) {
+                               try {
+                                       String s = pending_queue.take();
+                                       log_file.write(s);
+                                       log_file.write('\n');
+                               } catch (InterruptedException ie) {
+                               }
+                       }
+                       log_file.flush();
+                       file = a;
+               }
+               return log_file != null;
+       }
+
+       public void run () {
+               try {
+                       AltosRecord     previous = null;
+                       for (;;) {
+                               AltosLine       line = input_queue.take();
+                               if (line.line == null)
+                                       continue;
+                               try {
+                                       AltosRecord     telem = AltosTelemetry.parse(line.line, previous);
+                                       if (telem.serial != 0 && telem.flight != 0 &&
+                                           (telem.serial != serial || telem.flight != flight || log_file == null))
+                                       {
+                                               close_log_file();
+                                               serial = telem.serial;
+                                               flight = telem.flight;
+                                               open(telem);
+                                       }
+                                       previous = telem;
+                               } catch (ParseException pe) {
+                               } catch (AltosCRCException ce) {
+                               }
+                               if (log_file != null) {
+                                       log_file.write(line.line);
+                                       log_file.write('\n');
+                                       log_file.flush();
+                               } else
+                                       pending_queue.put(line.line);
+                       }
+               } catch (InterruptedException ie) {
+               } catch (IOException ie) {
+               }
+               close();
+       }
+
+       public AltosLog (AltosLink link) {
+               pending_queue = new LinkedBlockingQueue<String> ();
+               input_queue = new LinkedBlockingQueue<AltosLine> ();
+               link.add_monitor(input_queue);
+               serial = -1;
+               flight = -1;
+               log_file = null;
+               log_thread = new Thread(this);
+               log_thread.start();
+       }
+}
diff --git a/altoslib/AltosMag.java b/altoslib/AltosMag.java
new file mode 100644 (file)
index 0000000..0f8399a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosMag {
+       public int              x;
+       public int              y;
+       public int              z;
+}
+       
\ No newline at end of file
diff --git a/altoslib/AltosMs5607.java b/altoslib/AltosMs5607.java
new file mode 100644 (file)
index 0000000..268e89f
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosMs5607 {
+       public int      reserved;
+       public int      sens;
+       public int      off;
+       public int      tcs;
+       public int      tco;
+       public int      tref;
+       public int      tempsens;
+       public int      crc;
+
+       public int      raw_pres;
+       public int      raw_temp;
+       public int      pa;
+       public int      cc;
+
+       void convert() {
+               int     dT;
+               int TEMP;
+               long OFF;
+               long SENS;
+               //int P;
+
+               dT = raw_temp - ((int) tref << 8);
+       
+               TEMP = (int) (2000 + (((long) dT * tempsens) >> 23));
+
+               OFF = ((long) off << 17) + (((long) tco * dT) >> 6);
+
+               SENS = ((long) sens << 16) + (((long) tcs * dT) >> 7);
+
+               if (TEMP < 2000) {
+                       int     T2 = (int) (((long) dT * (long) dT) >> 31);
+                       int TEMPM = TEMP - 2000;
+                       long OFF2 = (61 * (long) TEMPM * (long) TEMPM) >> 4;
+                       long SENS2 = 2 * (long) TEMPM * (long) TEMPM;
+                       if (TEMP < 1500) {
+                               int TEMPP = TEMP + 1500;
+                               long TEMPP2 = TEMPP * TEMPP;
+                               OFF2 = OFF2 + 15 * TEMPP2;
+                               SENS2 = SENS2 + 8 * TEMPP2;
+                       }
+                       TEMP -= T2;
+                       OFF -= OFF2;
+                       SENS -= SENS2;
+               }
+
+               pa = (int) (((((long) raw_pres * SENS) >> 21) - OFF) >> 15);
+               cc = TEMP;
+       }
+
+       public int set(int in_pres, int in_temp) {
+               raw_pres = in_pres;
+               raw_temp = in_temp;
+               convert();
+               return pa;
+       }
+
+       public AltosMs5607() {
+               raw_pres = AltosRecord.MISSING;
+               raw_temp = AltosRecord.MISSING;
+               pa = AltosRecord.MISSING;
+               cc = AltosRecord.MISSING;
+       }
+}
diff --git a/altoslib/AltosMs5607Query.java b/altoslib/AltosMs5607Query.java
new file mode 100644 (file)
index 0000000..3c74679
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+import java.util.concurrent.TimeoutException;
+
+class AltosMs5607Query extends AltosMs5607 {
+       public AltosMs5607Query (AltosLink link) throws InterruptedException, TimeoutException {
+               link.printf("v\nB\n");
+               for (;;) {
+                       String line = link.get_reply_no_dialog(5000);
+                       if (line == null) {
+                               throw new TimeoutException();
+                       }
+                       String[] items = line.split("\\s+");
+                       if (line.startsWith("Pressure:")) {
+                               if (items.length >= 2)
+                                       raw_pres = Integer.parseInt(items[1]);
+                       } else if (line.startsWith("Temperature:")) {
+                               if (items.length >= 2)
+                                       raw_temp = Integer.parseInt(items[1]);
+                       } else if (line.startsWith("ms5607 reserved:")) {
+                               if (items.length >= 3)
+                                       reserved = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("ms5607 sens:")) {
+                               if (items.length >= 3)
+                                       sens = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("ms5607 off:")) {
+                               if (items.length >= 3)
+                                       off = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("ms5607 tcs:")) {
+                               if (items.length >= 3)
+                                       tcs = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("ms5607 tco:")) {
+                               if (items.length >= 3)
+                                       tco = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("ms5607 tref:")) {
+                               if (items.length >= 3)
+                                       tref = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("ms5607 tempsens:")) {
+                               if (items.length >= 3)
+                                       tempsens = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("ms5607 crc:")) {
+                               if (items.length >= 3)
+                                       crc = Integer.parseInt(items[2]);
+                       } else if (line.startsWith("Altitude"))
+                               break;
+               }
+               convert();
+       }
+}
+
diff --git a/altoslib/AltosOrderedMegaRecord.java b/altoslib/AltosOrderedMegaRecord.java
new file mode 100644 (file)
index 0000000..05423dd
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.ParseException;
+
+/*
+ * AltosRecords with an index field so they can be sorted by tick while preserving
+ * the original ordering for elements with matching ticks
+ */
+class AltosOrderedMegaRecord extends AltosEepromMega implements Comparable<AltosOrderedMegaRecord> {
+
+       public int      index;
+
+       public AltosOrderedMegaRecord(String line, int in_index, int prev_tick, boolean prev_tick_valid)
+               throws ParseException {
+               super(line);
+               if (prev_tick_valid) {
+                       tick |= (prev_tick & ~0xffff);
+                       if (tick < prev_tick) {
+                               if (prev_tick - tick > 0x8000)
+                                       tick += 0x10000;
+                       } else {
+                               if (tick - prev_tick > 0x8000)
+                                       tick -= 0x10000;
+                       }
+               }
+               index = in_index;
+       }
+
+       public AltosOrderedMegaRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) {
+               super(in_cmd, in_tick);
+               a = in_a;
+               b = in_b;
+               index = in_index;
+       }
+
+       public String toString() {
+               return String.format("%d.%d %04x %04x %04x",
+                                    cmd, index, tick, a, b);
+       }
+
+       public int compareTo(AltosOrderedMegaRecord o) {
+               int     tick_diff = tick - o.tick;
+               if (tick_diff != 0)
+                       return tick_diff;
+               return index - o.index;
+       }
+}
diff --git a/altoslib/AltosOrderedRecord.java b/altoslib/AltosOrderedRecord.java
new file mode 100644 (file)
index 0000000..b4cfd8f
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.ParseException;
+
+/*
+ * AltosRecords with an index field so they can be sorted by tick while preserving
+ * the original ordering for elements with matching ticks
+ */
+class AltosOrderedRecord extends AltosEepromRecord implements Comparable<AltosOrderedRecord> {
+
+       public int      index;
+
+       public AltosOrderedRecord(String line, int in_index, int prev_tick, boolean prev_tick_valid)
+               throws ParseException {
+               super(line);
+               if (prev_tick_valid) {
+                       tick |= (prev_tick & ~0xffff);
+                       if (tick < prev_tick) {
+                               if (prev_tick - tick > 0x8000)
+                                       tick += 0x10000;
+                       } else {
+                               if (tick - prev_tick > 0x8000)
+                                       tick -= 0x10000;
+                       }
+               }
+               index = in_index;
+       }
+
+       public AltosOrderedRecord(int in_cmd, int in_tick, int in_a, int in_b, int in_index) {
+               super(in_cmd, in_tick, in_a, in_b);
+               index = in_index;
+       }
+
+       public String toString() {
+               return String.format("%d.%d %04x %04x %04x",
+                                    cmd, index, tick, a, b);
+       }
+
+       public int compareTo(AltosOrderedRecord o) {
+               int     tick_diff = tick - o.tick;
+               if (tick_diff != 0)
+                       return tick_diff;
+               return index - o.index;
+       }
+}
+
diff --git a/altoslib/AltosParse.java b/altoslib/AltosParse.java
new file mode 100644 (file)
index 0000000..e938a17
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+public class AltosParse {
+       public static boolean isdigit(char c) {
+               return '0' <= c && c <= '9';
+       }
+
+       public static int parse_int(String v) throws ParseException {
+               try {
+                       return AltosLib.fromdec(v);
+               } catch (NumberFormatException e) {
+                       throw new ParseException("error parsing int " + v, 0);
+               }
+       }
+
+       public static int parse_hex(String v) throws ParseException {
+               try {
+                       return AltosLib.fromhex(v);
+               } catch (NumberFormatException e) {
+                       throw new ParseException("error parsing hex " + v, 0);
+               }
+       }
+
+       public static double parse_double(String v) throws ParseException {
+               try {
+                       return Double.parseDouble(v);
+               } catch (NumberFormatException e) {
+                       throw new ParseException("error parsing double " + v, 0);
+               }
+       }
+
+       public static double parse_coord(String coord) throws ParseException {
+               String[]        dsf = coord.split("\\D+");
+
+               if (dsf.length != 3) {
+                       throw new ParseException("error parsing coord " + coord, 0);
+               }
+               int deg = parse_int(dsf[0]);
+               int min = parse_int(dsf[1]);
+               int frac = parse_int(dsf[2]);
+
+               double r = deg + (min + frac / 10000.0) / 60.0;
+               if (coord.endsWith("S") || coord.endsWith("W"))
+                       r = -r;
+               return r;
+       }
+
+       public static String strip_suffix(String v, String suffix) {
+               if (v.endsWith(suffix))
+                       return v.substring(0, v.length() - suffix.length());
+               return v;
+       }
+
+       public static void word(String v, String m) throws ParseException {
+               if (!v.equals(m)) {
+                       throw new ParseException("error matching '" + v + "' '" + m + "'", 0);
+               }
+       }
+}
diff --git a/altoslib/AltosPreferences.java b/altoslib/AltosPreferences.java
new file mode 100644 (file)
index 0000000..065b6e9
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.*;
+import java.util.prefs.*;
+import javax.swing.filechooser.FileSystemView;
+
+public class AltosPreferences {
+       public static Preferences preferences;
+
+       /* logdir preference name */
+       public final static String logdirPreference = "LOGDIR";
+
+       /* channel preference name */
+       public final static String channelPreferenceFormat = "CHANNEL-%d";
+
+       /* frequency preference name */
+       public final static String frequencyPreferenceFormat = "FREQUENCY-%d";
+
+       /* telemetry format preference name */
+       public final static String telemetryPreferenceFormat = "TELEMETRY-%d";
+
+       /* voice preference name */
+       public final static String voicePreference = "VOICE";
+
+       /* callsign preference name */
+       public final static String callsignPreference = "CALLSIGN";
+
+       /* firmware directory preference name */
+       public final static String firmwaredirPreference = "FIRMWARE";
+
+       /* serial debug preference name */
+       public final static String serialDebugPreference = "SERIAL-DEBUG";
+
+       /* scanning telemetry preferences name */
+       public final static String scanningTelemetryPreference = "SCANNING-TELEMETRY";
+
+       /* Launcher serial preference name */
+       public final static String launcherSerialPreference = "LAUNCHER-SERIAL";
+
+       /* Launcher channel preference name */
+       public final static String launcherChannelPreference = "LAUNCHER-CHANNEL";
+       
+       /* Default logdir is ~/TeleMetrum */
+       public final static String logdirName = "TeleMetrum";
+
+       /* Log directory */
+       public static File logdir;
+
+       /* Map directory -- hangs of logdir */
+       public static File mapdir;
+
+       /* Frequency (map serial to frequency) */
+       public static Hashtable<Integer, Double> frequencies;
+
+       /* Telemetry (map serial to telemetry format) */
+       public static Hashtable<Integer, Integer> telemetries;
+
+       /* Voice preference */
+       public static boolean voice;
+
+       /* Callsign preference */
+       public static String callsign;
+
+       /* Firmware directory */
+       public static File firmwaredir;
+
+       /* Scanning telemetry */
+       public static int scanning_telemetry;
+
+       /* List of frequencies */
+       public final static String common_frequencies_node_name = "COMMON-FREQUENCIES";
+       public static AltosFrequency[] common_frequencies;
+
+       public final static String      frequency_count = "COUNT";
+       public final static String      frequency_format = "FREQUENCY-%d";
+       public final static String      description_format = "DESCRIPTION-%d";
+
+       /* Units preference */
+
+       public final static String      unitsPreference = "IMPERIAL-UNITS";
+
+       public static AltosFrequency[] load_common_frequencies() {
+               AltosFrequency[] frequencies = null;
+               boolean existing = false;
+               try {
+                       existing = preferences.nodeExists(common_frequencies_node_name);
+               } catch (BackingStoreException be) {
+                       existing = false;
+               }
+               if (existing) {
+                       Preferences     node = preferences.node(common_frequencies_node_name);
+                       int             count = node.getInt(frequency_count, 0);
+
+                       frequencies = new AltosFrequency[count];
+                       for (int i = 0; i < count; i++) {
+                               double  frequency;
+                               String  description;
+
+                               frequency = node.getDouble(String.format(frequency_format, i), 0.0);
+                               description = node.get(String.format(description_format, i), null);
+                               frequencies[i] = new AltosFrequency(frequency, description);
+                       }
+               } else {
+                       frequencies = new AltosFrequency[10];
+                       for (int i = 0; i < 10; i++) {
+                               frequencies[i] = new AltosFrequency(434.550 + i * .1,
+                                                                          String.format("Channel %d", i));
+                       }
+               }
+               return frequencies;
+       }
+
+       public static void save_common_frequencies(AltosFrequency[] frequencies) {
+               Preferences     node = preferences.node(common_frequencies_node_name);
+
+               node.putInt(frequency_count, frequencies.length);
+               for (int i = 0; i < frequencies.length; i++) {
+                       node.putDouble(String.format(frequency_format, i), frequencies[i].frequency);
+                       node.put(String.format(description_format, i), frequencies[i].description);
+               }
+       }
+       public static int launcher_serial;
+
+       public static int launcher_channel;
+
+       public static void init() {
+               preferences = Preferences.userRoot().node("/org/altusmetrum/altosui");
+
+               /* Initialize logdir from preferences */
+               String logdir_string = preferences.get(logdirPreference, null);
+               if (logdir_string != null)
+                       logdir = new File(logdir_string);
+               else {
+                       /* Use the file system view default directory */
+                       logdir = new File(FileSystemView.getFileSystemView().getDefaultDirectory(), logdirName);
+                       if (!logdir.exists())
+                               logdir.mkdirs();
+               }
+               mapdir = new File(logdir, "maps");
+               if (!mapdir.exists())
+                       mapdir.mkdirs();
+
+               frequencies = new Hashtable<Integer, Double>();
+
+               telemetries = new Hashtable<Integer,Integer>();
+
+               voice = preferences.getBoolean(voicePreference, true);
+
+               callsign = preferences.get(callsignPreference,"N0CALL");
+
+               scanning_telemetry = preferences.getInt(scanningTelemetryPreference,(1 << AltosLib.ao_telemetry_standard));
+
+               launcher_serial = preferences.getInt(launcherSerialPreference, 0);
+
+               launcher_channel = preferences.getInt(launcherChannelPreference, 0);
+
+               String firmwaredir_string = preferences.get(firmwaredirPreference, null);
+               if (firmwaredir_string != null)
+                       firmwaredir = new File(firmwaredir_string);
+               else
+                       firmwaredir = null;
+
+               common_frequencies = load_common_frequencies();
+
+               AltosConvert.imperial_units = preferences.getBoolean(unitsPreference, false);
+       }
+
+       static { init(); }
+
+       public static void flush_preferences() {
+               try {
+                       preferences.flush();
+               } catch (BackingStoreException ee) {
+/*
+                       if (component != null)
+                               JOptionPane.showMessageDialog(component,
+                                                             preferences.absolutePath(),
+                                                             "Cannot save prefernces",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       else
+*/
+                               System.err.printf("Cannot save preferences\n");
+               }
+       }
+
+       public static void set_logdir(File new_logdir) {
+               logdir = new_logdir;
+               mapdir = new File(logdir, "maps");
+               if (!mapdir.exists())
+                       mapdir.mkdirs();
+               synchronized (preferences) {
+                       preferences.put(logdirPreference, logdir.getPath());
+                       flush_preferences();
+               }
+       }
+
+       public static File logdir() {
+               return logdir;
+       }
+
+       public static File mapdir() {
+               return mapdir;
+       }
+
+       public static void set_frequency(int serial, double new_frequency) {
+               frequencies.put(serial, new_frequency);
+               synchronized (preferences) {
+                       preferences.putDouble(String.format(frequencyPreferenceFormat, serial), new_frequency);
+                       flush_preferences();
+               }
+       }
+
+       public static double frequency(int serial) {
+               if (frequencies.containsKey(serial))
+                       return frequencies.get(serial);
+               double frequency = preferences.getDouble(String.format(frequencyPreferenceFormat, serial), 0);
+               if (frequency == 0.0) {
+                       int channel = preferences.getInt(String.format(channelPreferenceFormat, serial), 0);
+                       frequency = AltosConvert.radio_channel_to_frequency(channel);
+               }
+               frequencies.put(serial, frequency);
+               return frequency;
+       }
+
+       public static void set_telemetry(int serial, int new_telemetry) {
+               telemetries.put(serial, new_telemetry);
+               synchronized (preferences) {
+                       preferences.putInt(String.format(telemetryPreferenceFormat, serial), new_telemetry);
+                       flush_preferences();
+               }
+       }
+
+       public static int telemetry(int serial) {
+               if (telemetries.containsKey(serial))
+                       return telemetries.get(serial);
+               int telemetry = preferences.getInt(String.format(telemetryPreferenceFormat, serial),
+                                                  AltosLib.ao_telemetry_standard);
+               telemetries.put(serial, telemetry);
+               return telemetry;
+       }
+
+       public static void set_scanning_telemetry(int new_scanning_telemetry) {
+               scanning_telemetry = new_scanning_telemetry;
+               synchronized (preferences) {
+                       preferences.putInt(scanningTelemetryPreference, scanning_telemetry);
+                       flush_preferences();
+               }
+       }
+
+       public static int scanning_telemetry() {
+               return scanning_telemetry;
+       }
+
+       public static void set_voice(boolean new_voice) {
+               voice = new_voice;
+               synchronized (preferences) {
+                       preferences.putBoolean(voicePreference, voice);
+                       flush_preferences();
+               }
+       }
+
+       public static boolean voice() {
+               return voice;
+       }
+
+       public static void set_callsign(String new_callsign) {
+               callsign = new_callsign;
+               synchronized(preferences) {
+                       preferences.put(callsignPreference, callsign);
+                       flush_preferences();
+               }
+       }
+
+       public static String callsign() {
+               return callsign;
+       }
+
+       public static void set_firmwaredir(File new_firmwaredir) {
+               firmwaredir = new_firmwaredir;
+               synchronized (preferences) {
+                       preferences.put(firmwaredirPreference, firmwaredir.getPath());
+                       flush_preferences();
+               }
+       }
+
+       public static File firmwaredir() {
+               return firmwaredir;
+       }
+
+       public static void set_launcher_serial(int new_launcher_serial) {
+               launcher_serial = new_launcher_serial;
+               synchronized (preferences) {
+                       preferences.putInt(launcherSerialPreference, launcher_serial);
+                       flush_preferences();
+               }
+       }
+
+       public static int launcher_serial() {
+               return launcher_serial;
+       }
+
+       public static void set_launcher_channel(int new_launcher_channel) {
+               launcher_channel = new_launcher_channel;
+               synchronized (preferences) {
+                       preferences.putInt(launcherChannelPreference, launcher_channel);
+                       flush_preferences();
+               }
+       }
+
+       public static int launcher_channel() {
+               return launcher_channel;
+       }
+       
+       public static Preferences bt_devices() {
+               return preferences.node("bt_devices");
+       }
+
+       public static AltosFrequency[] common_frequencies() {
+               return common_frequencies;
+       }
+
+       public static void set_common_frequencies(AltosFrequency[] frequencies) {
+               common_frequencies = frequencies;
+               synchronized(preferences) {
+                       save_common_frequencies(frequencies);
+                       flush_preferences();
+               }
+       }
+
+       public static void add_common_frequency(AltosFrequency frequency) {
+               AltosFrequency[]        new_frequencies = new AltosFrequency[common_frequencies.length + 1];
+               int                     i;
+
+               for (i = 0; i < common_frequencies.length; i++) {
+                       if (frequency.frequency == common_frequencies[i].frequency)
+                               return;
+                       if (frequency.frequency < common_frequencies[i].frequency)
+                               break;
+                       new_frequencies[i] = common_frequencies[i];
+               }
+               new_frequencies[i] = frequency;
+               for (; i < common_frequencies.length; i++)
+                       new_frequencies[i+1] = common_frequencies[i];
+               set_common_frequencies(new_frequencies);
+       }
+
+       public static boolean imperial_units() {
+               return AltosConvert.imperial_units;
+       }
+
+       public static void set_imperial_units(boolean imperial_units) {
+               AltosConvert.imperial_units = imperial_units;
+               synchronized (preferences) {
+                       preferences.putBoolean(unitsPreference, imperial_units);
+                       flush_preferences();
+               }
+       }
+}
diff --git a/altoslib/AltosRecord.java b/altoslib/AltosRecord.java
new file mode 100644 (file)
index 0000000..dd74171
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+public class AltosRecord implements Comparable <AltosRecord>, Cloneable {
+
+       public static final int seen_flight = 1;
+       public static final int seen_sensor = 2;
+       public static final int seen_temp_volt = 4;
+       public static final int seen_deploy = 8;
+       public static final int seen_gps_time = 16;
+       public static final int seen_gps_lat = 32;
+       public static final int seen_gps_lon = 64;
+       public static final int seen_companion = 128;
+
+       public int      seen;
+       
+       public final static int MISSING = 0x7fffffff;
+
+       /* Every AltosRecord implementation provides these fields */
+       
+       public int      version;
+       public String   callsign;
+       public int      serial;
+       public int      flight;
+       public int      rssi;
+       public int      status;
+       public int      state;
+       public int      tick;
+
+       /* Current flight dynamic state */
+       public double   acceleration;   /* m/s² */
+       public double   speed;          /* m/s */
+       public double   height;         /* m */
+
+       public AltosGPS gps;
+       public boolean  new_gps;
+
+       public double   time;   /* seconds since boost */
+
+       public int      device_type;
+       public int      config_major;
+       public int      config_minor;
+       public int      apogee_delay;
+       public int      main_deploy;
+       public int      flight_log_max;
+       public String   firmware_version;
+
+       public AltosRecordCompanion companion;
+
+       /*
+        * Abstract methods that convert record data
+        * to standard units:
+        *
+        *      pressure:       Pa
+        *      voltage:        V
+        *      acceleration:   m/s²
+        *      speed:          m/s
+        *      height:         m
+        *      temperature:    °C
+        */
+
+       public double raw_pressure() { return MISSING; }
+
+       public double filtered_pressure() { return MISSING; }
+
+       public double ground_pressure() { return MISSING; }
+
+       public double battery_voltage() { return MISSING; }
+
+       public double main_voltage() { return MISSING; }
+
+       public double drogue_voltage() { return MISSING; }
+
+       public double temperature() { return MISSING; }
+       
+       public double acceleration() { return MISSING; }
+
+       public double accel_speed() { return MISSING; }
+
+       public AltosIMU imu() { return null; }
+
+       public AltosMag mag() { return null; }
+
+       /*
+        * Convert various pressure values to altitude
+        */
+
+       public double raw_altitude() {
+               double p = raw_pressure();
+               if (p == MISSING)
+                       return MISSING;
+               return AltosConvert.pressure_to_altitude(p);
+       }
+
+       public double ground_altitude() {
+               double p = ground_pressure();
+               if (p == MISSING)
+                       return MISSING;
+               return AltosConvert.pressure_to_altitude(p);
+       }
+
+       public double filtered_altitude() {
+               double  ga = ground_altitude();
+               if (height != MISSING && ga != MISSING)
+                       return height + ga;
+
+               double  p = filtered_pressure();
+               if (p == MISSING)
+                       return raw_altitude();
+               return AltosConvert.pressure_to_altitude(p);
+       }
+
+       public double filtered_height() {
+               if (height != MISSING)
+                       return height;
+
+               double f = filtered_altitude();
+               double g = ground_altitude();
+               if (f == MISSING || g == MISSING)
+                       return MISSING;
+               return f - g;
+       }
+
+       public double raw_height() {
+               double r = raw_altitude();
+               double g = ground_altitude();
+
+               if (r == MISSING || g == MISSING)
+                       return height;
+               return r - g;
+       }
+
+       public String state() {
+               return AltosLib.state_name(state);
+       }
+
+       public int compareTo(AltosRecord o) {
+               return tick - o.tick;
+       }
+
+       public void copy(AltosRecord old) {
+               seen = old.seen;
+               version = old.version;
+               callsign = old.callsign;
+               serial = old.serial;
+               flight = old.flight;
+               rssi = old.rssi;
+               status = old.status;
+               state = old.state;
+               tick = old.tick;
+               acceleration = old.acceleration;
+               speed = old.speed;
+               height = old.height;
+               gps = new AltosGPS(old.gps);
+               new_gps = false;
+               companion = old.companion;
+       }
+
+       public AltosRecord clone() {
+               try {
+                       AltosRecord n = (AltosRecord) super.clone();
+                       n.copy(this);
+                       return n;
+               } catch (CloneNotSupportedException e) {
+                       return null;
+               }
+       }
+
+       public AltosRecord() {
+               seen = 0;
+               version = 0;
+               callsign = "N0CALL";
+               serial = 0;
+               flight = 0;
+               rssi = 0;
+               status = 0;
+               state = AltosLib.ao_flight_startup;
+               tick = 0;
+               acceleration = MISSING;
+               speed = MISSING;
+               height = MISSING;
+               gps = new AltosGPS();
+               new_gps = false;
+               companion = null;
+       }
+}
diff --git a/altoslib/AltosRecordCompanion.java b/altoslib/AltosRecordCompanion.java
new file mode 100644 (file)
index 0000000..c8cc6ca
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+public class AltosRecordCompanion {
+       public final static int board_id_telescience = 0x0a;
+       public final static int MAX_CHANNELS = 12;
+
+       public int      tick;
+       public int      board_id;
+       public int      update_period;
+       public int      channels;
+       public int[]    companion_data;
+
+       public AltosRecordCompanion(int in_channels) {
+               channels = in_channels;
+               if (channels < 0)
+                       channels = 0;
+               if (channels > MAX_CHANNELS)
+                       channels = MAX_CHANNELS;
+               companion_data = new int[channels];
+       }
+}
diff --git a/altoslib/AltosRecordIterable.java b/altoslib/AltosRecordIterable.java
new file mode 100644 (file)
index 0000000..ed1787e
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.*;
+
+public abstract class AltosRecordIterable implements Iterable<AltosRecord> {
+       public abstract Iterator<AltosRecord> iterator();
+       public void write_comments(PrintStream out) { }
+       public boolean has_accel() { return false; }
+       public boolean has_gps() { return false; }
+       public boolean has_ignite() { return false; };
+}
diff --git a/altoslib/AltosRecordMM.java b/altoslib/AltosRecordMM.java
new file mode 100644 (file)
index 0000000..5f952f7
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosRecordMM extends AltosRecord {
+
+       public int      accel;
+       public int      pres;
+       public int      temp;
+       
+       public int      v_batt;
+       public int      v_pyro;
+       public int      sense[];
+
+       public int      ground_accel;
+       public int      ground_pres;
+       public int      accel_plus_g;
+       public int      accel_minus_g;
+
+       public int      flight_accel;
+       public int      flight_vel;
+       public int      flight_pres;
+
+       public final static int num_sense = 6;
+
+       public AltosIMU imu;
+       public AltosMag mag;
+
+       static double adc(int raw) {
+               return raw / 4095.0;
+       }
+
+       public double raw_pressure() {
+               if (pres != MISSING)
+                       return pres;
+               return MISSING;
+       }
+
+       public double filtered_pressure() {
+               return raw_pressure();
+       }
+
+       public double ground_pressure() {
+               if (ground_pres != MISSING)
+                       return ground_pres;
+               return MISSING;
+       }
+
+       public double battery_voltage() {
+               if (v_batt != MISSING)
+                       return 3.3 * adc(v_batt) * (15.0 + 27.0) / 27.0;
+               return MISSING;
+       }
+
+       static double pyro(int raw) {
+               if (raw != MISSING)
+                       return 3.3 * adc(raw) * (100.0 + 27.0) / 27.0;
+               return MISSING;
+       }
+
+       public double main_voltage() {
+               return pyro(sense[1]);
+       }
+
+       public double drogue_voltage() {
+               return pyro(sense[0]);
+       }
+
+       public double temperature() {
+               if (temp != MISSING)
+                       return temp / 100.0;
+               return MISSING;
+       }
+       
+       public AltosIMU imu() { return imu; }
+
+       public AltosMag mag() { return mag; }
+
+       double accel_counts_per_mss() {
+               double  counts_per_g = Math.abs(accel_minus_g - accel_plus_g) / 2;
+
+               return counts_per_g / 9.80665;
+       }
+
+       public double acceleration() {
+               if (acceleration != MISSING)
+                       return acceleration;
+
+               if (ground_accel == MISSING || accel == MISSING)
+                       return MISSING;
+
+               if (accel_minus_g == MISSING || accel_plus_g == MISSING)
+                       return MISSING;
+
+               return (ground_accel - accel) / accel_counts_per_mss();
+       }
+
+       public double accel_speed() {
+               return speed;
+       }
+
+       public void copy (AltosRecordMM old) {
+               super.copy(old);
+
+               accel = old.accel;
+               pres = old.pres;
+               temp = old.temp;
+
+               v_batt = old.v_batt;
+               v_pyro = old.v_pyro;
+               sense = new int[num_sense];
+               
+               for (int i = 0; i < num_sense; i++)
+                       sense[i] = old.sense[i];
+
+               ground_accel = old.ground_accel;
+               ground_pres = old.ground_pres;
+               accel_plus_g = old.accel_plus_g;
+               accel_minus_g = old.accel_minus_g;
+               
+               flight_accel = old.flight_accel;
+               flight_vel = old.flight_vel;
+               flight_pres = old.flight_pres;
+
+               imu = old.imu;
+               mag = old.mag;
+       }
+
+       public AltosRecordMM clone() {
+               AltosRecordMM n = (AltosRecordMM) super.clone();
+               n.copy(this);
+               return n;
+       }
+
+       void make_missing() {
+
+               accel = MISSING;
+               pres = MISSING;
+               temp = MISSING;
+
+               v_batt = MISSING;
+               v_pyro = MISSING;
+               sense = new int[num_sense];
+               for (int i = 0; i < num_sense; i++)
+                       sense[i] = MISSING;
+
+               ground_accel = MISSING;
+               ground_pres = MISSING;
+               accel_plus_g = MISSING;
+               accel_minus_g = MISSING;
+
+               flight_accel = 0;
+               flight_vel = 0;
+               flight_pres = 0;
+
+               imu = new AltosIMU();
+               mag = new AltosMag();
+       }
+
+       public AltosRecordMM(AltosRecord old) {
+               super.copy(old);
+               make_missing();
+       }
+
+       public AltosRecordMM() {
+               super();
+               make_missing();
+       }
+}
diff --git a/altoslib/AltosRecordTM.java b/altoslib/AltosRecordTM.java
new file mode 100644 (file)
index 0000000..37accef
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosRecordTM extends AltosRecord {
+       public int      accel;
+       public int      pres;
+       public int      temp;
+       public int      batt;
+       public int      drogue;
+       public int      main;
+
+       public int      ground_accel;
+       public int      ground_pres;
+       public int      accel_plus_g;
+       public int      accel_minus_g;
+
+       public int      flight_accel;
+       public int      flight_vel;
+       public int      flight_pres;
+
+       /*
+        * Values for our MP3H6115A pressure sensor
+        *
+        * From the data sheet:
+        *
+        * Pressure range: 15-115 kPa
+        * Voltage at 115kPa: 2.82
+        * Output scale: 27mV/kPa
+        *
+        *
+        * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa
+        * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa
+        */
+
+       static final double counts_per_kPa = 27 * 2047 / 3300;
+       static final double counts_at_101_3kPa = 1674.0;
+
+       static double
+       barometer_to_pressure(double count)
+       {
+               return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0;
+       }
+
+       public double raw_pressure() {
+               if (pres == MISSING)
+                       return MISSING;
+               return barometer_to_pressure(pres);
+       }
+
+       public double filtered_pressure() {
+               if (flight_pres == MISSING)
+                       return MISSING;
+               return barometer_to_pressure(flight_pres);
+       }
+
+       public double ground_pressure() {
+               if (ground_pres == MISSING)
+                       return MISSING;
+               return barometer_to_pressure(ground_pres);
+       }
+
+       public double battery_voltage() {
+               if (batt == MISSING)
+                       return MISSING;
+               return AltosConvert.cc_battery_to_voltage(batt);
+       }
+
+       public double main_voltage() {
+               if (main == MISSING)
+                       return MISSING;
+               return AltosConvert.cc_ignitor_to_voltage(main);
+       }
+
+       public double drogue_voltage() {
+               if (drogue == MISSING)
+                       return MISSING;
+               return AltosConvert.cc_ignitor_to_voltage(drogue);
+       }
+
+       /* Value for the CC1111 built-in temperature sensor
+        * Output voltage at 0°C = 0.755V
+        * Coefficient = 0.00247V/°C
+        * Reference voltage = 1.25V
+        *
+        * temp = ((value / 32767) * 1.25 - 0.755) / 0.00247
+        *      = (value - 19791.268) / 32768 * 1.25 / 0.00247
+        */
+
+       static double
+       thermometer_to_temperature(double thermo)
+       {
+               return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247;
+       }
+
+       public double temperature() {
+               if (temp == MISSING)
+                       return MISSING;
+               return thermometer_to_temperature(temp);
+       }
+
+       double accel_counts_per_mss() {
+               double  counts_per_g = Math.abs(accel_minus_g - accel_plus_g) / 2;
+
+               return counts_per_g / 9.80665;
+       }
+
+       public double acceleration() {
+               if (acceleration != MISSING)
+                       return acceleration;
+
+               if (ground_accel == MISSING || accel == MISSING)
+                       return MISSING;
+               return (ground_accel - accel) / accel_counts_per_mss();
+       }
+
+       public double accel_speed() {
+               if (speed != MISSING)
+                       return speed;
+               if (flight_vel == MISSING)
+                       return MISSING;
+               return flight_vel / (accel_counts_per_mss() * 100.0);
+       }
+
+       public void copy(AltosRecordTM old) {
+               super.copy(old);
+
+               version = old.version;
+               callsign = old.callsign;
+               serial = old.serial;
+               flight = old.flight;
+               rssi = old.rssi;
+               status = old.status;
+               state = old.state;
+               tick = old.tick;
+               accel = old.accel;
+               pres = old.pres;
+               temp = old.temp;
+               batt = old.batt;
+               drogue = old.drogue;
+               main = old.main;
+               flight_accel = old.flight_accel;
+               ground_accel = old.ground_accel;
+               flight_vel = old.flight_vel;
+               flight_pres = old.flight_pres;
+               ground_pres = old.ground_pres;
+               accel_plus_g = old.accel_plus_g;
+               accel_minus_g = old.accel_minus_g;
+       }
+
+       public AltosRecordTM clone() {
+               AltosRecordTM   n = (AltosRecordTM) super.clone();
+               n.copy(this);
+               return n;
+       }
+
+       void make_missing() {
+               accel = MISSING;
+               pres = MISSING;
+               temp = MISSING;
+               batt = MISSING;
+               drogue = MISSING;
+               main = MISSING;
+
+               flight_accel = MISSING;
+               flight_vel = MISSING;
+               flight_pres = MISSING;
+
+               ground_accel = MISSING;
+               ground_pres = MISSING;
+               accel_plus_g = MISSING;
+               accel_minus_g = MISSING;
+       }
+
+       public AltosRecordTM(AltosRecord old) {
+               super.copy(old);
+               make_missing();
+       }
+
+       public AltosRecordTM() {
+               super();
+               make_missing();
+       }
+}
diff --git a/altoslib/AltosReplayReader.java b/altoslib/AltosReplayReader.java
new file mode 100644 (file)
index 0000000..50bef07
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.*;
+
+/*
+ * Open an existing telemetry file and replay it in realtime
+ */
+
+public class AltosReplayReader extends AltosFlightReader {
+       Iterator<AltosRecord>   iterator;
+       File    file;
+
+       public AltosRecord read() {
+               if (iterator.hasNext())
+                       return iterator.next();
+               return null;
+       }
+
+       public void close (boolean interrupted) {
+       }
+
+       public void update(AltosState state) throws InterruptedException {
+               /* Make it run in realtime after the rocket leaves the pad */
+               if (state.state > AltosLib.ao_flight_pad)
+                       Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
+       }
+
+       public File backing_file() { return file; }
+
+       public AltosReplayReader(Iterator<AltosRecord> in_iterator, File in_file) {
+               iterator = in_iterator;
+               file = in_file;
+               name = file.getName();
+       }
+}
diff --git a/altoslib/AltosSensorMM.java b/altoslib/AltosSensorMM.java
new file mode 100644 (file)
index 0000000..b6f21ef
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+import java.util.concurrent.TimeoutException;
+
+class AltosSensorMM {
+       int             tick;
+       int             sense[];
+       int             v_batt;
+       int             v_pyro;
+       int             accel;
+       int             accel_ref;
+
+       public AltosSensorMM(AltosLink link) throws InterruptedException, TimeoutException {
+               link.printf("a\n");
+               for (;;) {
+                       String line = link.get_reply_no_dialog(5000);
+                       if (line == null) {
+                               throw new TimeoutException();
+                       }
+                       if (!line.startsWith("tick:"))
+                               continue;
+                       String[] items = line.split("\\s+");
+                       sense = new int[6];
+                       for (int i = 0; i < items.length;) {
+                               if (items[i].equals("tick:")) {
+                                       tick = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("0:")) {
+                                       sense[0] = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("1:")) {
+                                       sense[1] = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("2:")) {
+                                       sense[2] = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("3:")) {
+                                       sense[3] = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("4:")) {
+                                       sense[4] = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("5:")) {
+                                       sense[5] = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("6:")) {
+                                       v_batt = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("7:")) {
+                                       v_pyro = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("8:")) {
+                                       accel = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("9:")) {
+                                       accel_ref = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               i++;
+                       }
+                       break;
+               }
+       }
+}
+
diff --git a/altoslib/AltosSensorTM.java b/altoslib/AltosSensorTM.java
new file mode 100644 (file)
index 0000000..75158cb
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+import java.util.concurrent.TimeoutException;
+
+class AltosSensorTM extends AltosRecordTM {
+
+       public AltosSensorTM(AltosLink link, AltosConfigData config_data) throws InterruptedException, TimeoutException {
+               super();
+               link.printf("a\n");
+               for (;;) {
+                       String line = link.get_reply_no_dialog(5000);
+                       if (line == null) {
+                               throw new TimeoutException();
+                       }
+                       if (!line.startsWith("tick:"))
+                               continue;
+                       String[] items = line.split("\\s+");
+                       for (int i = 0; i < items.length;) {
+                               if (items[i].equals("tick:")) {
+                                       tick = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("accel:")) {
+                                       accel = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("pres:")) {
+                                       pres = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("temp:")) {
+                                       temp = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("batt:")) {
+                                       batt = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("drogue:")) {
+                                       drogue = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               if (items[i].equals("main:")) {
+                                       main = Integer.parseInt(items[i+1]);
+                                       i += 2;
+                                       continue;
+                               }
+                               i++;
+                       }
+                       break;
+               }
+               ground_accel = config_data.accel_cal_plus;
+               ground_pres = pres;
+               accel_plus_g = config_data.accel_cal_plus;
+               accel_minus_g = config_data.accel_cal_minus;
+       }
+}
+
diff --git a/altoslib/AltosSpeed.java b/altoslib/AltosSpeed.java
new file mode 100644 (file)
index 0000000..af63ed1
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public class AltosSpeed extends AltosUnits {
+
+       public double value(double v) {
+               if (AltosConvert.imperial_units)
+                       return AltosConvert.meters_to_feet(v);
+               return v;
+       }
+
+       public String show_units() {
+               if (AltosConvert.imperial_units)
+                       return "ft/s";
+               return "m/s";
+       }
+
+       public String say_units() {
+               if (AltosConvert.imperial_units)
+                       return "feet per second";
+               return "meters per second";
+       }
+
+       int show_fraction(int width) {
+               return width / 9;
+       }
+}
\ No newline at end of file
diff --git a/altoslib/AltosState.java b/altoslib/AltosState.java
new file mode 100644 (file)
index 0000000..2806812
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+/*
+ * Track flight state from telemetry or eeprom data stream
+ */
+
+package org.altusmetrum.AltosLib;
+
+public class AltosState {
+       public AltosRecord data;
+
+       /* derived data */
+
+       public long     report_time;
+
+       public double   time;
+       public double   time_change;
+       public int      tick;
+
+       public int      state;
+       public boolean  landed;
+       public boolean  ascent; /* going up? */
+       public boolean boost;   /* under power */
+
+       public double   ground_altitude;
+       public double   altitude;
+       public double   height;
+       public double   speed;
+       public double   acceleration;
+       public double   battery;
+       public double   temperature;
+       public double   main_sense;
+       public double   drogue_sense;
+       public double   baro_speed;
+
+       public double   max_height;
+       public double   max_acceleration;
+       public double   max_speed;
+       public double   max_baro_speed;
+
+       public AltosGPS gps;
+
+       public AltosIMU imu;
+       public AltosMag mag;
+
+       public static final int MIN_PAD_SAMPLES = 10;
+
+       public int      npad;
+       public int      ngps;
+       public int      gps_waiting;
+       public boolean  gps_ready;
+
+       public AltosGreatCircle from_pad;
+       public double   elevation;      /* from pad */
+       public double   range;          /* total distance */
+
+       public double   gps_height;
+
+       public double pad_lat, pad_lon, pad_alt;
+
+       public int      speak_tick;
+       public double   speak_altitude;
+
+       public void init (AltosRecord cur, AltosState prev_state) {
+               //int           i;
+               //AltosRecord prev;
+
+               data = cur;
+
+               ground_altitude = data.ground_altitude();
+               altitude = data.raw_altitude();
+               height = data.filtered_height();
+
+               report_time = System.currentTimeMillis();
+
+               acceleration = data.acceleration();
+               speed = data.accel_speed();
+               temperature = data.temperature();
+               drogue_sense = data.drogue_voltage();
+               main_sense = data.main_voltage();
+               battery = data.battery_voltage();
+               tick = data.tick;
+               state = data.state;
+
+               if (prev_state != null) {
+
+                       /* Preserve any existing gps data */
+                       npad = prev_state.npad;
+                       ngps = prev_state.ngps;
+                       gps = prev_state.gps;
+                       pad_lat = prev_state.pad_lat;
+                       pad_lon = prev_state.pad_lon;
+                       pad_alt = prev_state.pad_alt;
+                       max_height = prev_state.max_height;
+                       max_acceleration = prev_state.max_acceleration;
+                       max_speed = prev_state.max_speed;
+                       max_baro_speed = prev_state.max_baro_speed;
+                       imu = prev_state.imu;
+                       mag = prev_state.mag;
+
+                       /* make sure the clock is monotonic */
+                       while (tick < prev_state.tick)
+                               tick += 65536;
+
+                       time_change = (tick - prev_state.tick) / 100.0;
+
+                       /* compute barometric speed */
+
+                       double height_change = height - prev_state.height;
+                       if (data.speed != AltosRecord.MISSING)
+                               baro_speed = data.speed;
+                       else {
+                               if (time_change > 0)
+                                       baro_speed = (prev_state.baro_speed * 3 + (height_change / time_change)) / 4.0;
+                               else
+                                       baro_speed = prev_state.baro_speed;
+                       }
+               } else {
+                       npad = 0;
+                       ngps = 0;
+                       gps = null;
+                       baro_speed = 0;
+                       time_change = 0;
+               }
+
+               time = tick / 100.0;
+
+               if (cur.new_gps && (state == AltosLib.ao_flight_pad || state == AltosLib.ao_flight_idle)) {
+
+                       /* Track consecutive 'good' gps reports, waiting for 10 of them */
+                       if (data.gps != null && data.gps.locked && data.gps.nsat >= 4)
+                               npad++;
+                       else
+                               npad = 0;
+
+                       /* Average GPS data while on the pad */
+                       if (data.gps != null && data.gps.locked && data.gps.nsat >= 4) {
+                               if (ngps > 1) {
+                                       /* filter pad position */
+                                       pad_lat = (pad_lat * 31.0 + data.gps.lat) / 32.0;
+                                       pad_lon = (pad_lon * 31.0 + data.gps.lon) / 32.0;
+                                       pad_alt = (pad_alt * 31.0 + data.gps.alt) / 32.0;
+                               } else {
+                                       pad_lat = data.gps.lat;
+                                       pad_lon = data.gps.lon;
+                                       pad_alt = data.gps.alt;
+                               }
+                               ngps++;
+                       }
+               } else
+                       pad_alt = ground_altitude;
+
+               gps_waiting = MIN_PAD_SAMPLES - npad;
+               if (gps_waiting < 0)
+                       gps_waiting = 0;
+
+               gps_ready = gps_waiting == 0;
+
+               ascent = (AltosLib.ao_flight_boost <= state &&
+                         state <= AltosLib.ao_flight_coast);
+               boost = (AltosLib.ao_flight_boost == state);
+
+               /* Only look at accelerometer data under boost */
+               if (boost && acceleration > max_acceleration && acceleration != AltosRecord.MISSING)
+                       max_acceleration = acceleration;
+               if (boost && speed > max_speed && speed != AltosRecord.MISSING)
+                       max_speed = speed;
+               if (boost && baro_speed > max_baro_speed && baro_speed != AltosRecord.MISSING)
+                       max_baro_speed = baro_speed;
+
+               if (height > max_height && height != AltosRecord.MISSING)
+                       max_height = height;
+               if (data.gps != null) {
+                       if (gps == null || !gps.locked || data.gps.locked)
+                               gps = data.gps;
+                       if (ngps > 0 && gps.locked) {
+                               from_pad = new AltosGreatCircle(pad_lat, pad_lon, gps.lat, gps.lon);
+                       }
+               }
+               elevation = 0;
+               range = -1;
+               if (ngps > 0) {
+                       gps_height = gps.alt - pad_alt;
+                       if (from_pad != null) {
+                               elevation = Math.atan2(height, from_pad.distance) * 180 / Math.PI;
+                               range = Math.sqrt(height * height + from_pad.distance * from_pad.distance);
+                       }
+               } else {
+                       gps_height = 0;
+               }
+       }
+
+       public AltosState(AltosRecord cur) {
+               init(cur, null);
+       }
+
+       public AltosState (AltosRecord cur, AltosState prev) {
+               init(cur, prev);
+       }
+}
diff --git a/altoslib/AltosTelemetry.java b/altoslib/AltosTelemetry.java
new file mode 100644 (file)
index 0000000..1553415
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+/*
+ * Telemetry data contents
+ */
+
+
+/*
+ * The packet format is a simple hex dump of the raw telemetry frame.
+ * It starts with 'TELEM', then contains hex digits with a checksum as the last
+ * byte on the line.
+ *
+ * Version 4 is a replacement with consistent syntax. Each telemetry line
+ * contains a sequence of space-separated names and values, the values are
+ * either integers or strings. The names are all unique. All values are
+ * optional
+ *
+ * VERSION 4 c KD7SQG n 236 f 18 r -25 s pad t 513 r_a 15756 r_b 26444 r_t 20944
+ *   r_v 26640 r_d 512 r_m 208 c_a 15775 c_b 26439 c_p 15749 c_m 16281 a_a 15764
+ *   a_s 0 a_b 26439 g_s u g_n 0 s_n 0
+ *
+ * VERSION 4 c KD7SQG n 19 f 0 r -23 s pad t 513 r_b 26372 r_t 21292 r_v 26788
+ *   r_d 136 r_m 140 c_b 26370 k_h 0 k_s 0 k_a 0
+ *
+ * General header fields
+ *
+ *     Name            Value
+ *
+ *     VERSION         Telemetry version number (4 or more). Must be first.
+ *     c               Callsign (string, no spaces allowed)
+ *     n               Flight unit serial number (integer)
+ *     f               Flight number (integer)
+ *     r               Packet RSSI value (integer)
+ *     s               Flight computer state (string, no spaces allowed)
+ *     t               Flight computer clock (integer in centiseconds)
+ *
+ * Version 3 is Version 2 with fixed RSSI numbers -- the radio reports
+ * in 1/2dB increments while this protocol provides only integers. So,
+ * the syntax didn't change just the interpretation of the RSSI
+ * values.
+ *
+ * Version 2 of the telemetry data stream is a bit of a mess, with no
+ * consistent formatting. In particular, the GPS data is formatted for
+ * viewing instead of parsing.  However, the key feature is that every
+ * telemetry line contains all of the information necessary to
+ * describe the current rocket state, including the calibration values
+ * for accelerometer and barometer.
+ *
+ * GPS unlocked:
+ *
+ * VERSION 2 CALL KB0G SERIAL  51 FLIGHT     2 RSSI  -68 STATUS ff STATE     pad  1001 \
+ *    a: 16032 p: 21232 t: 20284 v: 25160 d:   204 m:   204 fa: 16038 ga: 16032 fv:       0 \
+ *    fp: 21232 gp: 21230 a+: 16049 a-: 16304 GPS  0 sat unlocked SAT 1   15  30
+ *
+ * GPS locked:
+ *
+ * VERSION 2 CALL KB0G SERIAL  51 FLIGHT     2 RSSI  -71 STATUS ff STATE     pad  2504 \
+ *     a: 16028 p: 21220 t: 20360 v: 25004 d:   208 m:   200 fa: 16031 ga: 16032 fv:     330 \
+ *     fp: 21231 gp: 21230 a+: 16049 a-: 16304 \
+ *     GPS  9 sat 2010-02-13 17:16:51 35°20.0803'N 106°45.2235'W  1790m  \
+ *     0.00m/s(H) 0°     0.00m/s(V) 1.0(hdop)     0(herr)     0(verr) \
+ *     SAT 10   29  30  24  28   5  25  21  20  15  33   1  23  30  24  18  26  10  29   2  26
+ *
+ */
+
+public abstract class AltosTelemetry extends AltosRecord {
+
+       /*
+        * General header fields
+        *
+        *      Name            Value
+        *
+        *      VERSION         Telemetry version number (4 or more). Must be first.
+        *      c               Callsign (string, no spaces allowed)
+        *      n               Flight unit serial number (integer)
+        *      f               Flight number (integer)
+        *      r               Packet RSSI value (integer)
+        *      s               Flight computer state (string, no spaces allowed)
+        *      t               Flight computer clock (integer in centiseconds)
+        */
+
+       final static String AO_TELEM_VERSION    = "VERSION";
+       final static String AO_TELEM_CALL       = "c";
+       final static String AO_TELEM_SERIAL     = "n";
+       final static String AO_TELEM_FLIGHT     = "f";
+       final static String AO_TELEM_RSSI       = "r";
+       final static String AO_TELEM_STATE      = "s";
+       final static String AO_TELEM_TICK       = "t";
+
+       /*
+        * Raw sensor values
+        *
+        *      Name            Value
+        *      r_a             Accelerometer reading (integer)
+        *      r_b             Barometer reading (integer)
+        *      r_t             Thermometer reading (integer)
+        *      r_v             Battery reading (integer)
+        *      r_d             Drogue continuity (integer)
+        *      r_m             Main continuity (integer)
+        */
+
+       final static String AO_TELEM_RAW_ACCEL  = "r_a";
+       final static String AO_TELEM_RAW_BARO   = "r_b";
+       final static String AO_TELEM_RAW_THERMO = "r_t";
+       final static String AO_TELEM_RAW_BATT   = "r_v";
+       final static String AO_TELEM_RAW_DROGUE = "r_d";
+       final static String AO_TELEM_RAW_MAIN   = "r_m";
+
+       /*
+        * Sensor calibration values
+        *
+        *      Name            Value
+        *      c_a             Ground accelerometer reading (integer)
+        *      c_b             Ground barometer reading (integer)
+        *      c_p             Accelerometer reading for +1g
+        *      c_m             Accelerometer reading for -1g
+        */
+
+       final static String AO_TELEM_CAL_ACCEL_GROUND   = "c_a";
+       final static String AO_TELEM_CAL_BARO_GROUND    = "c_b";
+       final static String AO_TELEM_CAL_ACCEL_PLUS     = "c_p";
+       final static String AO_TELEM_CAL_ACCEL_MINUS    = "c_m";
+
+       /*
+        * Kalman state values
+        *
+        *      Name            Value
+        *      k_h             Height above pad (integer, meters)
+        *      k_s             Vertical speeed (integer, m/s * 16)
+        *      k_a             Vertical acceleration (integer, m/s² * 16)
+        */
+
+       final static String AO_TELEM_KALMAN_HEIGHT      = "k_h";
+       final static String AO_TELEM_KALMAN_SPEED       = "k_s";
+       final static String AO_TELEM_KALMAN_ACCEL       = "k_a";
+
+       /*
+        * Ad-hoc flight values
+        *
+        *      Name            Value
+        *      a_a             Acceleration (integer, sensor units)
+        *      a_s             Speed (integer, integrated acceleration value)
+        *      a_b             Barometer reading (integer, sensor units)
+        */
+
+       final static String AO_TELEM_ADHOC_ACCEL        = "a_a";
+       final static String AO_TELEM_ADHOC_SPEED        = "a_s";
+       final static String AO_TELEM_ADHOC_BARO         = "a_b";
+
+       /*
+        * GPS values
+        *
+        *      Name            Value
+        *      g_s             GPS state (string):
+        *                              l       locked
+        *                              u       unlocked
+        *                              e       error (missing or broken)
+        *      g_n             Number of sats used in solution
+        *      g_ns            Latitude (degrees * 10e7)
+        *      g_ew            Longitude (degrees * 10e7)
+        *      g_a             Altitude (integer meters)
+        *      g_Y             GPS year (integer)
+        *      g_M             GPS month (integer - 1-12)
+        *      g_D             GPS day (integer - 1-31)
+        *      g_h             GPS hour (integer - 0-23)
+        *      g_m             GPS minute (integer - 0-59)
+        *      g_s             GPS second (integer - 0-59)
+        *      g_v             GPS vertical speed (integer, cm/sec)
+        *      g_s             GPS horizontal speed (integer, cm/sec)
+        *      g_c             GPS course (integer, 0-359)
+        *      g_hd            GPS hdop (integer * 10)
+        *      g_vd            GPS vdop (integer * 10)
+        *      g_he            GPS h error (integer)
+        *      g_ve            GPS v error (integer)
+        */
+
+       final static String AO_TELEM_GPS_STATE                  = "g";
+       final static String AO_TELEM_GPS_STATE_LOCKED           = "l";
+       final static String AO_TELEM_GPS_STATE_UNLOCKED         = "u";
+       final static String AO_TELEM_GPS_STATE_ERROR            = "e";
+       final static String AO_TELEM_GPS_NUM_SAT                = "g_n";
+       final static String AO_TELEM_GPS_LATITUDE               = "g_ns";
+       final static String AO_TELEM_GPS_LONGITUDE              = "g_ew";
+       final static String AO_TELEM_GPS_ALTITUDE               = "g_a";
+       final static String AO_TELEM_GPS_YEAR                   = "g_Y";
+       final static String AO_TELEM_GPS_MONTH                  = "g_M";
+       final static String AO_TELEM_GPS_DAY                    = "g_D";
+       final static String AO_TELEM_GPS_HOUR                   = "g_h";
+       final static String AO_TELEM_GPS_MINUTE                 = "g_m";
+       final static String AO_TELEM_GPS_SECOND                 = "g_s";
+       final static String AO_TELEM_GPS_VERTICAL_SPEED         = "g_v";
+       final static String AO_TELEM_GPS_HORIZONTAL_SPEED       = "g_g";
+       final static String AO_TELEM_GPS_COURSE                 = "g_c";
+       final static String AO_TELEM_GPS_HDOP                   = "g_hd";
+       final static String AO_TELEM_GPS_VDOP                   = "g_vd";
+       final static String AO_TELEM_GPS_HERROR                 = "g_he";
+       final static String AO_TELEM_GPS_VERROR                 = "g_ve";
+
+       /*
+        * GPS satellite values
+        *
+        *      Name            Value
+        *      s_n             Number of satellites reported (integer)
+        *      s_v0            Space vehicle ID (integer) for report 0
+        *      s_c0            C/N0 number (integer) for report 0
+        *      s_v1            Space vehicle ID (integer) for report 1
+        *      s_c1            C/N0 number (integer) for report 1
+        *      ...
+        */
+
+       final static String AO_TELEM_SAT_NUM    = "s_n";
+       final static String AO_TELEM_SAT_SVID   = "s_v";
+       final static String AO_TELEM_SAT_C_N_0  = "s_c";
+
+       static public AltosRecord parse(String line, AltosRecord previous) throws ParseException, AltosCRCException {
+               AltosTelemetryRecord    r = AltosTelemetryRecord.parse(line);
+
+               return r.update_state(previous);
+       }
+}
diff --git a/altoslib/AltosTelemetryIterable.java b/altoslib/AltosTelemetryIterable.java
new file mode 100644 (file)
index 0000000..e95c15e
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+
+public class AltosTelemetryIterable extends AltosRecordIterable {
+       TreeSet<AltosRecord>    records;
+
+       public Iterator<AltosRecord> iterator () {
+               return records.iterator();
+       }
+
+       boolean has_gps = false;
+       boolean has_accel = false;
+       boolean has_ignite = false;
+       public boolean has_gps() { return has_gps; }
+       public boolean has_accel() { return has_accel; }
+       public boolean has_ignite() { return has_ignite; };
+
+       public AltosTelemetryIterable (FileInputStream input) {
+               boolean saw_boost = false;
+               int     current_tick = 0;
+               int     boost_tick = 0;
+
+               AltosRecord     previous = null;
+               records = new TreeSet<AltosRecord> ();
+
+               try {
+                       for (;;) {
+                               String line = AltosLib.gets(input);
+                               if (line == null) {
+                                       break;
+                               }
+                               try {
+                                       AltosRecord record = AltosTelemetry.parse(line, previous);
+                                       if (record == null)
+                                               break;
+                                       if (records.isEmpty()) {
+                                               current_tick = record.tick;
+                                       } else {
+                                               int tick = record.tick;
+                                               while (tick < current_tick - 0x1000)
+                                                       tick += 0x10000;
+                                               current_tick = tick;
+                                               record.tick = current_tick;
+                                       }
+                                       if (!saw_boost && record.state >= AltosLib.ao_flight_boost)
+                                       {
+                                               saw_boost = true;
+                                               boost_tick = record.tick;
+                                       }
+                                       if (record.acceleration() != AltosRecord.MISSING)
+                                               has_accel = true;
+                                       if (record.gps != null)
+                                               has_gps = true;
+                                       if (record.main_voltage() != AltosRecord.MISSING)
+                                               has_ignite = true;
+                                       if (previous != null && previous.tick != record.tick)
+                                               records.add(previous);
+                                       previous = record;
+                               } catch (ParseException pe) {
+                                       System.out.printf("parse exception %s\n", pe.getMessage());
+                               } catch (AltosCRCException ce) {
+                               }
+                       }
+               } catch (IOException io) {
+                       System.out.printf("io exception\n");
+               }
+
+               if (previous != null)
+                       records.add(previous);
+
+               /* Adjust all tick counts to match expected eeprom values,
+                * which starts with a 16-bit tick count 16 samples before boost
+                */
+
+               int tick_adjust = (boost_tick - 16) & 0xffff0000;
+               for (AltosRecord r : this)
+                       r.tick -= tick_adjust;
+               boost_tick -= tick_adjust;
+
+               /* adjust all tick counts to be relative to boost time */
+               for (AltosRecord r : this)
+                       r.time = (r.tick - boost_tick) / 100.0;
+
+               try {
+                       input.close();
+               } catch (IOException ie) {
+               }
+       }
+}
diff --git a/altoslib/AltosTelemetryMap.java b/altoslib/AltosTelemetryMap.java
new file mode 100644 (file)
index 0000000..bc1486d
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+import java.text.*;
+import java.util.HashMap;
+
+public class AltosTelemetryMap extends HashMap<String,String> {
+       public boolean has(String key) {
+               return containsKey(key);
+       }
+
+       public String get_string(String key) throws ParseException {
+               if (!has(key))
+                       throw new ParseException ("missing " + key, 0);
+               return (String) get(key);
+       }
+
+       public String get_string(String key, String def) {
+               if (has(key))
+                       return get(key);
+               else
+                       return def;
+       }
+
+       public int get_int(String key) throws ParseException {
+               return AltosParse.parse_int(get_string(key));
+       }
+
+       public int get_int(String key, int def) throws ParseException {
+               if (has(key))
+                       return get_int(key);
+               else
+                       return def;
+       }
+
+       public double get_double(String key, double def, double scale) throws ParseException {
+               if (has(key))
+                       return get_int(key) * scale;
+               else
+                       return def;
+       }
+
+       public AltosTelemetryMap(String[] words, int start) {
+               for (int i = start; i < words.length - 1; i += 2)
+                       put(words[i], words[i+1]);
+       }
+}
diff --git a/altoslib/AltosTelemetryReader.java b/altoslib/AltosTelemetryReader.java
new file mode 100644 (file)
index 0000000..94fa560
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+import java.io.*;
+import java.util.concurrent.*;
+
+public class AltosTelemetryReader extends AltosFlightReader {
+       AltosLink       link;
+       AltosLog        log;
+       AltosRecord     previous;
+       double          frequency;
+       int             telemetry;
+
+       LinkedBlockingQueue<AltosLine> telem;
+
+       public AltosRecord read() throws InterruptedException, ParseException, AltosCRCException, IOException {
+               AltosLine l = telem.take();
+               if (l.line == null)
+                       throw new IOException("IO error");
+               AltosRecord     next = AltosTelemetry.parse(l.line, previous);
+               previous = next;
+               return next;
+       }
+
+       public void flush() {
+               telem.clear();
+       }
+
+       public void reset() {
+               previous = null;
+               flush();
+       }
+
+       public void close(boolean interrupted) {
+               link.remove_monitor(telem);
+               log.close();
+               link.close();
+       }
+
+       public void set_frequency(double in_frequency) throws InterruptedException, TimeoutException {
+               frequency = in_frequency;
+               link.set_radio_frequency(frequency);
+       }
+
+       public boolean supports_telemetry(int telemetry) {
+
+               try {
+                       /* Version 1.0 or later firmware supports all telemetry formats */
+                       if (link.config_data().compare_version("1.0") >= 0)
+                               return true;
+
+                       /* Version 0.9 firmware only supports 0.9 telemetry */
+                       if (link.config_data().compare_version("0.9") >= 0) {
+                               if (telemetry == AltosLib.ao_telemetry_0_9)
+                                       return true;
+                               else
+                                       return false;
+                       }
+
+                       /* Version 0.8 firmware only supports 0.8 telemetry */
+                       if (telemetry == AltosLib.ao_telemetry_0_8)
+                               return true;
+                       else
+                               return false;
+               } catch (InterruptedException ie) {
+                       return true;
+               } catch (TimeoutException te) {
+                       return true;
+               }
+       }
+
+       public void save_frequency() {
+               AltosPreferences.set_frequency(link.serial, frequency);
+       }
+
+       public void set_telemetry(int in_telemetry) {
+               telemetry = in_telemetry;
+               link.set_telemetry(telemetry);
+       }
+
+       public void save_telemetry() {
+               AltosPreferences.set_telemetry(link.serial, telemetry);
+       }
+
+       public void set_monitor(boolean monitor) {
+               link.set_monitor(monitor);
+       }
+
+       public File backing_file() {
+               return log.file();
+       }
+
+       public AltosTelemetryReader (AltosLink in_link)
+               throws IOException, InterruptedException, TimeoutException {
+               link = in_link;
+               try {
+                       log = new AltosLog(link);
+                       name = link.name;
+                       previous = null;
+                       telem = new LinkedBlockingQueue<AltosLine>();
+                       frequency = AltosPreferences.frequency(link.serial);
+                       set_frequency(frequency);
+                       telemetry = AltosPreferences.telemetry(link.serial);
+                       set_telemetry(telemetry);
+                       link.add_monitor(telem);
+               } catch (TimeoutException e) {
+                       close(true);
+                       throw(e);
+               } catch (InterruptedException e) {
+                       close(true);
+                       throw(e);
+               }
+       }
+}
diff --git a/altoslib/AltosTelemetryRecord.java b/altoslib/AltosTelemetryRecord.java
new file mode 100644 (file)
index 0000000..6a8cfd3
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+import java.text.*;
+
+public abstract class AltosTelemetryRecord {
+
+       long    received_time;
+       abstract public AltosRecord update_state(AltosRecord previous);
+
+       static boolean cksum(int[] bytes) {
+               int     sum = 0x5a;
+               for (int i = 1; i < bytes.length - 1; i++)
+                       sum += bytes[i];
+               sum &= 0xff;
+               return sum == bytes[bytes.length - 1];
+       }
+
+       final static int PKT_APPEND_STATUS_1_CRC_OK             = (1 << 7);
+       final static int PKT_APPEND_STATUS_1_LQI_MASK           = (0x7f);
+       final static int PKT_APPEND_STATUS_1_LQI_SHIFT          = 0;
+
+       final static int packet_type_TM_sensor = 0x01;
+       final static int packet_type_Tm_sensor = 0x02;
+       final static int packet_type_Tn_sensor = 0x03;
+       final static int packet_type_configuration = 0x04;
+       final static int packet_type_location = 0x05;
+       final static int packet_type_satellite = 0x06;
+       final static int packet_type_companion = 0x07;
+       final static int packet_type_MM_sensor = 0x08;
+       final static int packet_type_MM_data = 0x09;
+       
+       static AltosTelemetryRecord parse_hex(String hex)  throws ParseException, AltosCRCException {
+               AltosTelemetryRecord    r;
+
+               int[] bytes;
+               try {
+                       bytes = AltosLib.hexbytes(hex);
+               } catch (NumberFormatException ne) {
+                       throw new ParseException(ne.getMessage(), 0);
+               }
+
+               /* one for length, one for checksum */
+               if (bytes[0] != bytes.length - 2)
+                       throw new ParseException(String.format("invalid length %d != %d\n",
+                                                              bytes[0],
+                                                              bytes.length - 2), 0);
+               if (!cksum(bytes))
+                       throw new ParseException(String.format("invalid line \"%s\"", hex), 0);
+
+               int     rssi = AltosLib.int8(bytes, bytes.length - 3) / 2 - 74;
+               int     status = AltosLib.uint8(bytes, bytes.length - 2);
+
+               if ((status & PKT_APPEND_STATUS_1_CRC_OK) == 0)
+                       throw new AltosCRCException(rssi);
+
+               /* length, data ..., rssi, status, checksum -- 4 bytes extra */
+               switch (bytes.length) {
+               case AltosLib.ao_telemetry_standard_len + 4:
+                       int     type = AltosLib.uint8(bytes, 4 + 1);
+                       switch (type) {
+                       case packet_type_TM_sensor:
+                       case packet_type_Tm_sensor:
+                       case packet_type_Tn_sensor:
+                               r = new AltosTelemetryRecordSensor(bytes, rssi);
+                               break;
+                       case packet_type_configuration:
+                               r = new AltosTelemetryRecordConfiguration(bytes);
+                               break;
+                       case packet_type_location:
+                               r = new AltosTelemetryRecordLocation(bytes);
+                               break;
+                       case packet_type_satellite:
+                               r = new AltosTelemetryRecordSatellite(bytes);
+                               break;
+                       case packet_type_companion:
+                               r = new AltosTelemetryRecordCompanion(bytes);
+                               break;
+                       case packet_type_MM_sensor:
+                               r = new AltosTelemetryRecordMegaSensor(bytes, rssi);
+                               break;
+                       case packet_type_MM_data:
+                               r = new AltosTelemetryRecordMegaData(bytes);
+                               break;
+                       default:
+                               r = new AltosTelemetryRecordRaw(bytes);
+                               break;
+                       }
+                       break;
+               case AltosLib.ao_telemetry_0_9_len + 4:
+                       r = new AltosTelemetryRecordLegacy(bytes, rssi, status);
+                       break;
+               case AltosLib.ao_telemetry_0_8_len + 4:
+                       r = new AltosTelemetryRecordLegacy(bytes, rssi, status);
+                       break;
+               default:
+                       throw new ParseException(String.format("Invalid packet length %d", bytes.length), 0);
+               }
+               r.received_time = System.currentTimeMillis();
+               return r;
+       }
+
+       public static AltosTelemetryRecord parse(String line) throws ParseException, AltosCRCException {
+               AltosTelemetryRecord    r;
+
+               String[] word = line.split("\\s+");
+               int i =0;
+               if (word[i].equals("CRC") && word[i+1].equals("INVALID")) {
+                       i += 2;
+                       AltosParse.word(word[i++], "RSSI");
+                       throw new AltosCRCException(AltosParse.parse_int(word[i++]));
+               }
+
+               if (word[i].equals("TELEM"))
+                       r = parse_hex(word[i+1]);
+               else
+                       r = new AltosTelemetryRecordLegacy(line);
+               return r;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordCompanion.java b/altoslib/AltosTelemetryRecordCompanion.java
new file mode 100644 (file)
index 0000000..6ad1724
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+public class AltosTelemetryRecordCompanion extends AltosTelemetryRecordRaw {
+
+       AltosRecordCompanion    companion;
+
+       public AltosTelemetryRecordCompanion(int[] in_bytes) {
+               super(in_bytes);
+
+               int     off = 0;
+               if (uint8(6) == 0)
+                       off = 1;
+               int channels = uint8(7+off);
+
+               if (off != 0 && channels >= 12)
+                       channels = 11;
+
+               companion = new AltosRecordCompanion(channels);
+               companion.tick          = tick;
+               companion.board_id      = uint8(5);
+               companion.update_period = uint8(6+off);
+               for (int i = 0; i < companion.companion_data.length; i++)
+                       companion.companion_data[i] = uint16(8 + off + i * 2);
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     next = super.update_state(previous);
+
+               next.companion = companion;
+               next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt;
+
+               companion.tick = tick;
+               return next;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordConfiguration.java b/altoslib/AltosTelemetryRecordConfiguration.java
new file mode 100644 (file)
index 0000000..25242ed
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+
+public class AltosTelemetryRecordConfiguration extends AltosTelemetryRecordRaw {
+       int     device_type;
+       int     flight;
+       int     config_major;
+       int     config_minor;
+       int     apogee_delay;
+       int     main_deploy;
+       int     flight_log_max;
+       String  callsign;
+       String  version;
+
+       public AltosTelemetryRecordConfiguration(int[] in_bytes) {
+               super(in_bytes);
+
+               device_type    = uint8(5);
+               flight         = uint16(6);
+               config_major   = uint8(8);
+               config_minor   = uint8(9);
+               apogee_delay   = uint16(10);
+               main_deploy    = uint16(12);
+               flight_log_max = uint16(14);
+               callsign       = string(16, 8);
+               version        = string(24, 8);
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     next = super.update_state(previous);
+
+               next.device_type = device_type;
+               next.flight = flight;
+               next.config_major = config_major;
+               next.config_minor = config_minor;
+               next.apogee_delay = apogee_delay;
+               next.main_deploy = main_deploy;
+               next.flight_log_max = flight_log_max;
+
+               next.callsign = callsign;
+               next.firmware_version = version;
+
+               next.seen |= AltosRecord.seen_deploy | AltosRecord.seen_flight;
+
+               return next;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordGeneral.java b/altoslib/AltosTelemetryRecordGeneral.java
new file mode 100644 (file)
index 0000000..a53280c
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+public class AltosTelemetryRecordGeneral {
+
+       static AltosTelemetryRecord parse(String line) throws ParseException, AltosCRCException {
+               AltosTelemetryRecord    r;
+
+               String[] word = line.split("\\s+");
+               int i =0;
+               if (word[i].equals("CRC") && word[i+1].equals("INVALID")) {
+                       i += 2;
+                       AltosParse.word(word[i++], "RSSI");
+                       throw new AltosCRCException(AltosParse.parse_int(word[i++]));
+               }
+
+               if (word[i].equals("TELEM"))
+                       r = AltosTelemetryRecordRaw.parse(word[i+1]);
+               else
+                       r = new AltosTelemetryRecordLegacy(line);
+               return r;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordLegacy.java b/altoslib/AltosTelemetryRecordLegacy.java
new file mode 100644 (file)
index 0000000..2117606
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * 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 org.altusmetrum.AltosLib;
+
+import java.text.*;
+
+/*
+ * Telemetry data contents
+ */
+
+
+/*
+ * The packet format is a simple hex dump of the raw telemetry frame.
+ * It starts with 'TELEM', then contains hex digits with a checksum as the last
+ * byte on the line.
+ *
+ * Version 4 is a replacement with consistent syntax. Each telemetry line
+ * contains a sequence of space-separated names and values, the values are
+ * either integers or strings. The names are all unique. All values are
+ * optional
+ *
+ * VERSION 4 c KD7SQG n 236 f 18 r -25 s pad t 513 r_a 15756 r_b 26444 r_t 20944
+ *   r_v 26640 r_d 512 r_m 208 c_a 15775 c_b 26439 c_p 15749 c_m 16281 a_a 15764
+ *   a_s 0 a_b 26439 g_s u g_n 0 s_n 0
+ *
+ * VERSION 4 c KD7SQG n 19 f 0 r -23 s pad t 513 r_b 26372 r_t 21292 r_v 26788
+ *   r_d 136 r_m 140 c_b 26370 k_h 0 k_s 0 k_a 0
+ *
+ * General header fields
+ *
+ *     Name            Value
+ *
+ *     VERSION         Telemetry version number (4 or more). Must be first.
+ *     c               Callsign (string, no spaces allowed)
+ *     n               Flight unit serial number (integer)
+ *     f               Flight number (integer)
+ *     r               Packet RSSI value (integer)
+ *     s               Flight computer state (string, no spaces allowed)
+ *     t               Flight computer clock (integer in centiseconds)
+ *
+ * Version 3 is Version 2 with fixed RSSI numbers -- the radio reports
+ * in 1/2dB increments while this protocol provides only integers. So,
+ * the syntax didn't change just the interpretation of the RSSI
+ * values.
+ *
+ * Version 2 of the telemetry data stream is a bit of a mess, with no
+ * consistent formatting. In particular, the GPS data is formatted for
+ * viewing instead of parsing.  However, the key feature is that every
+ * telemetry line contains all of the information necessary to
+ * describe the current rocket state, including the calibration values
+ * for accelerometer and barometer.
+ *
+ * GPS unlocked:
+ *
+ * VERSION 2 CALL KB0G SERIAL  51 FLIGHT     2 RSSI  -68 STATUS ff STATE     pad  1001 \
+ *    a: 16032 p: 21232 t: 20284 v: 25160 d:   204 m:   204 fa: 16038 ga: 16032 fv:       0 \
+ *    fp: 21232 gp: 21230 a+: 16049 a-: 16304 GPS  0 sat unlocked SAT 1   15  30
+ *
+ * GPS locked:
+ *
+ * VERSION 2 CALL KB0G SERIAL  51 FLIGHT     2 RSSI  -71 STATUS ff STATE     pad  2504 \
+ *     a: 16028 p: 21220 t: 20360 v: 25004 d:   208 m:   200 fa: 16031 ga: 16032 fv:     330 \
+ *     fp: 21231 gp: 21230 a+: 16049 a-: 16304 \
+ *     GPS  9 sat 2010-02-13 17:16:51 35°20.0803'N 106°45.2235'W  1790m  \
+ *     0.00m/s(H) 0°     0.00m/s(V) 1.0(hdop)     0(herr)     0(verr) \
+ *     SAT 10   29  30  24  28   5  25  21  20  15  33   1  23  30  24  18  26  10  29   2  26
+ *
+ */
+
+public class AltosTelemetryRecordLegacy extends AltosTelemetryRecord {
+       /*
+        * General header fields
+        *
+        *      Name            Value
+        *
+        *      VERSION         Telemetry version number (4 or more). Must be first.
+        *      c               Callsign (string, no spaces allowed)
+        *      n               Flight unit serial number (integer)
+        *      f               Flight number (integer)
+        *      r               Packet RSSI value (integer)
+        *      s               Flight computer state (string, no spaces allowed)
+        *      t               Flight computer clock (integer in centiseconds)
+        */
+
+       final static String AO_TELEM_VERSION    = "VERSION";
+       final static String AO_TELEM_CALL       = "c";
+       final static String AO_TELEM_SERIAL     = "n";
+       final static String AO_TELEM_FLIGHT     = "f";
+       final static String AO_TELEM_RSSI       = "r";
+       final static String AO_TELEM_STATE      = "s";
+       final static String AO_TELEM_TICK       = "t";
+
+       /*
+        * Raw sensor values
+        *
+        *      Name            Value
+        *      r_a             Accelerometer reading (integer)
+        *      r_b             Barometer reading (integer)
+        *      r_t             Thermometer reading (integer)
+        *      r_v             Battery reading (integer)
+        *      r_d             Drogue continuity (integer)
+        *      r_m             Main continuity (integer)
+        */
+
+       final static String AO_TELEM_RAW_ACCEL  = "r_a";
+       final static String AO_TELEM_RAW_BARO   = "r_b";
+       final static String AO_TELEM_RAW_THERMO = "r_t";
+       final static String AO_TELEM_RAW_BATT   = "r_v";
+       final static String AO_TELEM_RAW_DROGUE = "r_d";
+       final static String AO_TELEM_RAW_MAIN   = "r_m";
+
+       /*
+        * Sensor calibration values
+        *
+        *      Name            Value
+        *      c_a             Ground accelerometer reading (integer)
+        *      c_b             Ground barometer reading (integer)
+        *      c_p             Accelerometer reading for +1g
+        *      c_m             Accelerometer reading for -1g
+        */
+
+       final static String AO_TELEM_CAL_ACCEL_GROUND   = "c_a";
+       final static String AO_TELEM_CAL_BARO_GROUND    = "c_b";
+       final static String AO_TELEM_CAL_ACCEL_PLUS     = "c_p";
+       final static String AO_TELEM_CAL_ACCEL_MINUS    = "c_m";
+
+       /*
+        * Kalman state values
+        *
+        *      Name            Value
+        *      k_h             Height above pad (integer, meters)
+        *      k_s             Vertical speeed (integer, m/s * 16)
+        *      k_a             Vertical acceleration (integer, m/s² * 16)
+        */
+
+       final static String AO_TELEM_KALMAN_HEIGHT      = "k_h";
+       final static String AO_TELEM_KALMAN_SPEED       = "k_s";
+       final static String AO_TELEM_KALMAN_ACCEL       = "k_a";
+
+       /*
+        * Ad-hoc flight values
+        *
+        *      Name            Value
+        *      a_a             Acceleration (integer, sensor units)
+        *      a_s             Speed (integer, integrated acceleration value)
+        *      a_b             Barometer reading (integer, sensor units)
+        */
+
+       final static String AO_TELEM_ADHOC_ACCEL        = "a_a";
+       final static String AO_TELEM_ADHOC_SPEED        = "a_s";
+       final static String AO_TELEM_ADHOC_BARO         = "a_b";
+
+       /*
+        * GPS values
+        *
+        *      Name            Value
+        *      g_s             GPS state (string):
+        *                              l       locked
+        *                              u       unlocked
+        *                              e       error (missing or broken)
+        *      g_n             Number of sats used in solution
+        *      g_ns            Latitude (degrees * 10e7)
+        *      g_ew            Longitude (degrees * 10e7)
+        *      g_a             Altitude (integer meters)
+        *      g_Y             GPS year (integer)
+        *      g_M             GPS month (integer - 1-12)
+        *      g_D             GPS day (integer - 1-31)
+        *      g_h             GPS hour (integer - 0-23)
+        *      g_m             GPS minute (integer - 0-59)
+        *      g_s             GPS second (integer - 0-59)
+        *      g_v             GPS vertical speed (integer, cm/sec)
+        *      g_s             GPS horizontal speed (integer, cm/sec)
+        *      g_c             GPS course (integer, 0-359)
+        *      g_hd            GPS hdop (integer * 10)
+        *      g_vd            GPS vdop (integer * 10)
+        *      g_he            GPS h error (integer)
+        *      g_ve            GPS v error (integer)
+        */
+
+       final static String AO_TELEM_GPS_STATE                  = "g";
+       final static String AO_TELEM_GPS_STATE_LOCKED           = "l";
+       final static String AO_TELEM_GPS_STATE_UNLOCKED         = "u";
+       final static String AO_TELEM_GPS_STATE_ERROR            = "e";
+       final static String AO_TELEM_GPS_NUM_SAT                = "g_n";
+       final static String AO_TELEM_GPS_LATITUDE               = "g_ns";
+       final static String AO_TELEM_GPS_LONGITUDE              = "g_ew";
+       final static String AO_TELEM_GPS_ALTITUDE               = "g_a";
+       final static String AO_TELEM_GPS_YEAR                   = "g_Y";
+       final static String AO_TELEM_GPS_MONTH                  = "g_M";
+       final static String AO_TELEM_GPS_DAY                    = "g_D";
+       final static String AO_TELEM_GPS_HOUR                   = "g_h";
+       final static String AO_TELEM_GPS_MINUTE                 = "g_m";
+       final static String AO_TELEM_GPS_SECOND                 = "g_s";
+       final static String AO_TELEM_GPS_VERTICAL_SPEED         = "g_v";
+       final static String AO_TELEM_GPS_HORIZONTAL_SPEED       = "g_g";
+       final static String AO_TELEM_GPS_COURSE                 = "g_c";
+       final static String AO_TELEM_GPS_HDOP                   = "g_hd";
+       final static String AO_TELEM_GPS_VDOP                   = "g_vd";
+       final static String AO_TELEM_GPS_HERROR                 = "g_he";
+       final static String AO_TELEM_GPS_VERROR                 = "g_ve";
+
+       /*
+        * GPS satellite values
+        *
+        *      Name            Value
+        *      s_n             Number of satellites reported (integer)
+        *      s_v0            Space vehicle ID (integer) for report 0
+        *      s_c0            C/N0 number (integer) for report 0
+        *      s_v1            Space vehicle ID (integer) for report 1
+        *      s_c1            C/N0 number (integer) for report 1
+        *      ...
+        */
+
+       final static String AO_TELEM_SAT_NUM    = "s_n";
+       final static String AO_TELEM_SAT_SVID   = "s_v";
+       final static String AO_TELEM_SAT_C_N_0  = "s_c";
+
+       AltosRecordTM   record;
+
+       private void parse_v4(String[] words, int i) throws ParseException {
+               AltosTelemetryMap       map = new AltosTelemetryMap(words, i);
+
+               record.callsign = map.get_string(AO_TELEM_CALL, "N0CALL");
+               record.serial = map.get_int(AO_TELEM_SERIAL, AltosRecord.MISSING);
+               record.flight = map.get_int(AO_TELEM_FLIGHT, AltosRecord.MISSING);
+               record.rssi = map.get_int(AO_TELEM_RSSI, AltosRecord.MISSING);
+               record.state = AltosLib.state(map.get_string(AO_TELEM_STATE, "invalid"));
+               record.tick = map.get_int(AO_TELEM_TICK, 0);
+
+               /* raw sensor values */
+               record.accel = map.get_int(AO_TELEM_RAW_ACCEL, AltosRecord.MISSING);
+               record.pres = map.get_int(AO_TELEM_RAW_BARO, AltosRecord.MISSING);
+               record.temp = map.get_int(AO_TELEM_RAW_THERMO, AltosRecord.MISSING);
+               record.batt = map.get_int(AO_TELEM_RAW_BATT, AltosRecord.MISSING);
+               record.drogue = map.get_int(AO_TELEM_RAW_DROGUE, AltosRecord.MISSING);
+               record.main = map.get_int(AO_TELEM_RAW_MAIN, AltosRecord.MISSING);
+
+               /* sensor calibration information */
+               record.ground_accel = map.get_int(AO_TELEM_CAL_ACCEL_GROUND, AltosRecord.MISSING);
+               record.ground_pres = map.get_int(AO_TELEM_CAL_BARO_GROUND, AltosRecord.MISSING);
+               record.accel_plus_g = map.get_int(AO_TELEM_CAL_ACCEL_PLUS, AltosRecord.MISSING);
+               record.accel_minus_g = map.get_int(AO_TELEM_CAL_ACCEL_MINUS, AltosRecord.MISSING);
+
+               /* flight computer values */
+               record.acceleration = map.get_double(AO_TELEM_KALMAN_ACCEL, AltosRecord.MISSING, 1/16.0);
+               record.speed = map.get_double(AO_TELEM_KALMAN_SPEED, AltosRecord.MISSING, 1/16.0);
+               record.height = map.get_int(AO_TELEM_KALMAN_HEIGHT, AltosRecord.MISSING);
+
+               record.flight_accel = map.get_int(AO_TELEM_ADHOC_ACCEL, AltosRecord.MISSING);
+               record.flight_vel = map.get_int(AO_TELEM_ADHOC_SPEED, AltosRecord.MISSING);
+               record.flight_pres = map.get_int(AO_TELEM_ADHOC_BARO, AltosRecord.MISSING);
+
+               if (map.has(AO_TELEM_GPS_STATE)) {
+               record.gps = new AltosGPS(map);
+               record.new_gps = true;
+               }
+               else
+               record.gps = null;
+       }
+
+       private void parse_legacy(String[] words, int i) throws ParseException {
+
+               AltosParse.word (words[i++], "CALL");
+               record.callsign = words[i++];
+
+               AltosParse.word (words[i++], "SERIAL");
+               record.serial = AltosParse.parse_int(words[i++]);
+
+               if (record.version >= 2) {
+                       AltosParse.word (words[i++], "FLIGHT");
+                       record.flight = AltosParse.parse_int(words[i++]);
+               } else
+                       record.flight = 0;
+
+               AltosParse.word(words[i++], "RSSI");
+               record.rssi = AltosParse.parse_int(words[i++]);
+
+               /* Older telemetry data had mis-computed RSSI value */
+               if (record.version <= 2)
+                       record.rssi = (record.rssi + 74) / 2 - 74;
+
+               AltosParse.word(words[i++], "STATUS");
+               record.status = AltosParse.parse_hex(words[i++]);
+
+               AltosParse.word(words[i++], "STATE");
+               record.state = AltosLib.state(words[i++]);
+
+               record.tick = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "a:");
+               record.accel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "p:");
+               record.pres = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "t:");
+               record.temp = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "v:");
+               record.batt = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "d:");
+               record.drogue = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "m:");
+               record.main = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "fa:");
+               record.flight_accel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "ga:");
+               record.ground_accel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "fv:");
+               record.flight_vel = AltosParse.parse_int(words[i++]);
+
+               AltosParse.word(words[i++], "fp:");
+               record.flight_pres = AltosParse.parse_int(words[i++]);
+
+               /* Old TeleDongle code with kalman-reporting TeleMetrum code */
+               if ((record.flight_vel & 0xffff0000) == 0x80000000) {
+                       record.speed = ((short) record.flight_vel) / 16.0;
+                       record.acceleration = record.flight_accel / 16.0;
+                       record.height = record.flight_pres;
+                       record.flight_vel = AltosRecord.MISSING;
+                       record.flight_pres = AltosRecord.MISSING;
+                       record.flight_accel = AltosRecord.MISSING;
+               }
+
+               AltosParse.word(words[i++], "gp:");
+               record.ground_pres = AltosParse.parse_int(words[i++]);
+
+               if (record.version >= 1) {
+                       AltosParse.word(words[i++], "a+:");
+                       record.accel_plus_g = AltosParse.parse_int(words[i++]);
+
+                       AltosParse.word(words[i++], "a-:");
+                       record.accel_minus_g = AltosParse.parse_int(words[i++]);
+               } else {
+                       record.accel_plus_g = record.ground_accel;
+                       record.accel_minus_g = record.ground_accel + 530;
+               }
+
+               record.gps = new AltosGPS(words, i, record.version);
+               record.new_gps = true;
+       }
+
+       public AltosTelemetryRecordLegacy(String line) throws ParseException, AltosCRCException {
+               String[] words = line.split("\\s+");
+               int     i = 0;
+
+               record = new AltosRecordTM();
+
+               if (words[i].equals("CRC") && words[i+1].equals("INVALID")) {
+                       i += 2;
+                       AltosParse.word(words[i++], "RSSI");
+                       record.rssi = AltosParse.parse_int(words[i++]);
+                       throw new AltosCRCException(record.rssi);
+               }
+               if (words[i].equals("CALL")) {
+                       record.version = 0;
+               } else {
+                       AltosParse.word (words[i++], "VERSION");
+                       record.version = AltosParse.parse_int(words[i++]);
+               }
+
+               if (record.version < 4)
+                       parse_legacy(words, i);
+               else
+                       parse_v4(words, i);
+       }
+
+       /*
+        * Given a hex dump of a legacy telemetry line, construct an AltosRecordTM from that
+        */
+
+       int[]   bytes;
+       int     adjust;
+
+       /*
+       private int int8(int i) {
+               return AltosLib.int8(bytes, i + 1 + adjust);
+       }
+       */
+       private int uint8(int i) {
+               return AltosLib.uint8(bytes, i + 1 + adjust);
+       }
+       private int int16(int i) {
+               return AltosLib.int16(bytes, i + 1 + adjust);
+       }
+       private int uint16(int i) {
+               return AltosLib.uint16(bytes, i + 1 + adjust);
+       }
+       private int uint32(int i) {
+               return AltosLib.uint32(bytes, i + 1 + adjust);
+       }
+       private String string(int i, int l) {
+               return AltosLib.string(bytes, i + 1 + adjust, l);
+       }
+
+       static final int AO_GPS_NUM_SAT_MASK    = (0xf << 0);
+       static final int AO_GPS_NUM_SAT_SHIFT   = (0);
+
+       static final int AO_GPS_VALID           = (1 << 4);
+       static final int AO_GPS_RUNNING         = (1 << 5);
+       static final int AO_GPS_DATE_VALID      = (1 << 6);
+       static final int AO_GPS_COURSE_VALID    = (1 << 7);
+
+       public AltosTelemetryRecordLegacy(int[] in_bytes, int in_rssi, int in_status) {
+               record = new AltosRecordTM();
+
+               bytes = in_bytes;
+               record.version = 4;
+               adjust = 0;
+
+               if (bytes.length == AltosLib.ao_telemetry_0_8_len + 4) {
+                       record.serial = uint8(0);
+                       adjust = -1;
+               } else
+                       record.serial = uint16(0);
+
+               record.seen = AltosRecord.seen_flight | AltosRecord.seen_sensor | AltosRecord.seen_temp_volt | AltosRecord.seen_deploy;
+
+               record.callsign = string(62, 8);
+               record.flight = uint16(2);
+               record.rssi = in_rssi;
+               record.status = in_status;
+               record.state = uint8(4);
+               record.tick = uint16(21);
+               record.accel = int16(23);
+               record.pres = int16(25);
+               record.temp = int16(27);
+               record.batt = int16(29);
+               record.drogue = int16(31);
+               record.main = int16(33);
+               
+               record.ground_accel = int16(7);
+               record.ground_pres = int16(15);
+               record.accel_plus_g = int16(17);
+               record.accel_minus_g = int16(19);
+
+               if (uint16(11) == 0x8000) {
+                       record.acceleration = int16(5);
+                       record.speed = int16(9);
+                       record.height = int16(13);
+                       record.flight_accel = AltosRecord.MISSING;
+                       record.flight_vel = AltosRecord.MISSING;
+                       record.flight_pres = AltosRecord.MISSING;
+               } else {
+                       record.flight_accel = int16(5);
+                       record.flight_vel = uint32(9);
+                       record.flight_pres = int16(13);
+                       record.acceleration = AltosRecord.MISSING;
+                       record.speed = AltosRecord.MISSING;
+                       record.height = AltosRecord.MISSING;
+               }
+
+               record.gps = null;
+
+               int gps_flags = uint8(41);
+
+               if ((gps_flags & (AO_GPS_VALID|AO_GPS_RUNNING)) != 0) {
+                       record.gps = new AltosGPS();
+                       record.new_gps = true;
+
+                       record.seen |= AltosRecord.seen_gps_time | AltosRecord.seen_gps_lat | AltosRecord.seen_gps_lon;
+                       record.gps.nsat = (gps_flags & AO_GPS_NUM_SAT_MASK);
+                       record.gps.locked = (gps_flags & AO_GPS_VALID) != 0;
+                       record.gps.connected = true;
+                       record.gps.lat = uint32(42) / 1.0e7;
+                       record.gps.lon = uint32(46) / 1.0e7;
+                       record.gps.alt = int16(50);
+                       record.gps.ground_speed = uint16(52) / 100.0;
+                       record.gps.course = uint8(54) * 2;
+                       record.gps.hdop = uint8(55) / 5.0;
+                       record.gps.h_error = uint16(58);
+                       record.gps.v_error = uint16(60);
+
+                       int     n_tracking_reported = uint8(70);
+                       if (n_tracking_reported > 12)
+                               n_tracking_reported = 12;
+                       int     n_tracking_actual = 0;
+                       for (int i = 0; i < n_tracking_reported; i++) {
+                               if (uint8(71 + i*2) != 0)
+                                       n_tracking_actual++;
+                       }
+                       if (n_tracking_actual > 0) {
+                               record.gps.cc_gps_sat = new AltosGPSSat[n_tracking_actual];
+
+                               n_tracking_actual = 0;
+                               for (int i = 0; i < n_tracking_reported; i++) {
+                                       int     svid = uint8(71 + i*2);
+                                       int     c_n0 = uint8(72 + i*2);
+                                       if (svid != 0)
+                                               record.gps.cc_gps_sat[n_tracking_actual++] = new AltosGPSSat(svid, c_n0);
+                               }
+                       }
+               }
+
+               record.time = 0.0;
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               return record;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordLocation.java b/altoslib/AltosTelemetryRecordLocation.java
new file mode 100644 (file)
index 0000000..cddb773
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+
+public class AltosTelemetryRecordLocation extends AltosTelemetryRecordRaw {
+       int     flags;
+       int     altitude;
+       int     latitude;
+       int     longitude;
+       int     year;
+       int     month;
+       int     day;
+       int     hour;
+       int     minute;
+       int     second;
+       int     pdop;
+       int     hdop;
+       int     vdop;
+       int     mode;
+       int     ground_speed;
+       int     climb_rate;
+       int     course;
+
+       public AltosTelemetryRecordLocation(int[] in_bytes) {
+               super(in_bytes);
+
+               flags          = uint8(5);
+               altitude       = int16(6);
+               latitude       = uint32(8);
+               longitude      = uint32(12);
+               year           = uint8(16);
+               month          = uint8(17);
+               day            = uint8(18);
+               hour           = uint8(19);
+               minute         = uint8(20);
+               second         = uint8(21);
+               pdop           = uint8(22);
+               hdop           = uint8(23);
+               vdop           = uint8(24);
+               mode           = uint8(25);
+               ground_speed   = uint16(26);
+               climb_rate     = int16(28);
+               course         = uint8(30);
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     next = super.update_state(previous);
+
+               if (next.gps == null)
+                       next.gps = new AltosGPS();
+
+               next.gps.nsat = flags & 0xf;
+               next.gps.locked = (flags & (1 << 4)) != 0;
+               next.gps.connected = (flags & (1 << 5)) != 0;
+
+               if (next.gps.locked) {
+                       next.gps.lat = latitude * 1.0e-7;
+                       next.gps.lon = longitude * 1.0e-7;
+                       next.gps.alt = altitude;
+                       next.gps.year = 2000 + year;
+                       next.gps.month = month;
+                       next.gps.day = day;
+                       next.gps.hour = hour;
+                       next.gps.minute = minute;
+                       next.gps.second = second;
+                       next.gps.ground_speed = ground_speed * 1.0e-2;
+                       next.gps.course = course * 2;
+                       next.gps.climb_rate = climb_rate * 1.0e-2;
+                       next.gps.hdop = hdop;
+                       next.gps.vdop = vdop;
+                       next.seen |= AltosRecord.seen_gps_time | AltosRecord.seen_gps_lat | AltosRecord.seen_gps_lon;
+                       next.new_gps = true;
+               }
+
+               return next;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordMegaData.java b/altoslib/AltosTelemetryRecordMegaData.java
new file mode 100644 (file)
index 0000000..8f55d23
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+
+public class AltosTelemetryRecordMegaData extends AltosTelemetryRecordRaw {
+
+       int     state;
+       
+       int     v_batt;
+       int     v_pyro;
+       int     sense[];
+
+       int     ground_pres;
+       int     ground_accel;
+       int     accel_plus_g;
+       int     accel_minus_g;
+
+       int     acceleration;
+       int     speed;
+       int     height;
+
+       public AltosTelemetryRecordMegaData(int[] in_bytes) {
+               super(in_bytes);
+
+               state = int8(5);
+
+               v_batt = int16(6);
+               v_pyro = int16(8);
+
+               sense = new int[6];     
+
+               for (int i = 0; i < 6; i++) {
+                       sense[i] = int8(10 + i) << 4;
+                       sense[i] |= sense[i] >> 8;
+               }
+
+               ground_pres = int32(16);
+               ground_accel = int16(20);
+               accel_plus_g = int16(22);
+               accel_minus_g = int16(24);
+
+               acceleration = int16(26);
+               speed = int16(28);
+               height = int16(30);
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     n = super.update_state(previous);
+
+               AltosRecordMM   next;
+               if (!(n instanceof AltosRecordMM)) {
+                       next = new AltosRecordMM(n);
+               } else {
+                       next = (AltosRecordMM) n;
+               }
+
+               next.state = state;
+
+               next.v_batt = v_batt;
+               next.v_pyro = v_pyro;
+
+               for (int i = 0; i < 6; i++)
+                       next.sense[i] = sense[i];
+
+               next.ground_accel = ground_accel;
+               next.ground_pres = ground_pres;
+               next.accel_plus_g = accel_plus_g;
+               next.accel_minus_g = accel_minus_g;
+
+               next.acceleration = acceleration / 16.0;
+               next.speed = speed / 16.0;
+               next.height = height;
+
+               next.seen |= AltosRecord.seen_flight | AltosRecord.seen_temp_volt;
+
+               return next;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordMegaSensor.java b/altoslib/AltosTelemetryRecordMegaSensor.java
new file mode 100644 (file)
index 0000000..93c001d
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+
+public class AltosTelemetryRecordMegaSensor extends AltosTelemetryRecordRaw {
+       int     accel;
+       int     pres;
+       int     temp;
+
+       int     accel_x;
+       int     accel_y;
+       int     accel_z;
+
+       int     gyro_x;
+       int     gyro_y;
+       int     gyro_z;
+
+       int     mag_x;
+       int     mag_y;
+       int     mag_z;
+
+       int     rssi;
+
+       public AltosTelemetryRecordMegaSensor(int[] in_bytes, int in_rssi) {
+               super(in_bytes);
+
+               accel         = int16(6);
+               pres          = int32(8);
+               temp          = int16(12);
+
+               accel_x       = int16(14);
+               accel_y       = int16(16);
+               accel_z       = int16(18);
+
+               gyro_x        = int16(20);
+               gyro_y        = int16(22);
+               gyro_z        = int16(24);
+
+               mag_x         = int16(26);
+               mag_y         = int16(28);
+               mag_z         = int16(30);
+
+               rssi          = in_rssi;
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     n = super.update_state(previous);
+
+               AltosRecordMM   next;
+               if (!(n instanceof AltosRecordMM)) {
+                       next = new AltosRecordMM(n);
+               } else {
+                       next = (AltosRecordMM) n;
+               }
+
+               next.accel = accel;
+               next.pres = pres;
+               next.temp = temp;
+
+               next.imu.accel_x = accel_x;
+               next.imu.accel_y = accel_y;
+               next.imu.accel_z = accel_z;
+
+               next.imu.gyro_x = gyro_x;
+               next.imu.gyro_y = gyro_y;
+               next.imu.gyro_z = gyro_z;
+
+               next.mag.x = mag_x;
+               next.mag.y = mag_y;
+               next.mag.z = mag_z;
+
+               next.rssi = rssi;
+
+               next.seen |= AltosRecord.seen_sensor;
+
+               return next;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordRaw.java b/altoslib/AltosTelemetryRecordRaw.java
new file mode 100644 (file)
index 0000000..fbb373d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+public class AltosTelemetryRecordRaw extends AltosTelemetryRecord {
+       int[]   bytes;
+       int     serial;
+       int     tick;
+       int     type;
+
+       long    received_time;
+
+       public int int8(int off) {
+               return AltosLib.int8(bytes, off + 1);
+       }
+
+       public int uint8(int off) {
+               return AltosLib.uint8(bytes, off + 1);
+       }
+
+       public int int16(int off) {
+               return AltosLib.int16(bytes, off + 1);
+       }
+
+       public int uint16(int off) {
+               return AltosLib.uint16(bytes, off + 1);
+       }
+
+       public int uint32(int off) {
+               return AltosLib.uint32(bytes, off + 1);
+       }
+
+       public int int32(int off) {
+               return AltosLib.int32(bytes, off + 1);
+       }
+
+       public String string(int off, int l) {
+               return AltosLib.string(bytes, off + 1, l);
+       }
+
+       public AltosTelemetryRecordRaw(int[] in_bytes) {
+               bytes = in_bytes;
+               serial = uint16(0);
+               tick   = uint16(2);
+               type   = uint8(4);
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     next;
+               if (previous != null)
+                       next = previous.clone();
+               else
+                       next = new AltosRecord();
+               next.serial = serial;
+               next.tick = tick;
+               return next;
+       }
+
+       public long received_time() {
+               return received_time;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordSatellite.java b/altoslib/AltosTelemetryRecordSatellite.java
new file mode 100644 (file)
index 0000000..2526afb
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+public class AltosTelemetryRecordSatellite extends AltosTelemetryRecordRaw {
+       int             channels;
+       AltosGPSSat[]   sats;
+
+       public AltosTelemetryRecordSatellite(int[] in_bytes) {
+               super(in_bytes);
+
+               channels = uint8(5);
+               if (channels > 12)
+                       channels = 12;
+               if (channels == 0)
+                       sats = null;
+               else {
+                       sats = new AltosGPSSat[channels];
+                       for (int i = 0; i < channels; i++) {
+                               int     svid =  uint8(6 + i * 2 + 0);
+                               int     c_n_1 = uint8(6 + i * 2 + 1);
+                               sats[i] = new AltosGPSSat(svid, c_n_1);
+                       }
+               }
+       }
+
+       public AltosRecord update_state(AltosRecord previous) {
+               AltosRecord     next = super.update_state(previous);
+
+               if (next.gps == null)
+                       next.gps = new AltosGPS();
+
+               next.gps.cc_gps_sat = sats;
+
+               return next;
+       }
+}
diff --git a/altoslib/AltosTelemetryRecordSensor.java b/altoslib/AltosTelemetryRecordSensor.java
new file mode 100644 (file)
index 0000000..319a91b
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib;
+
+
+public class AltosTelemetryRecordSensor extends AltosTelemetryRecordRaw {
+       int     state;
+       int     accel;
+       int     pres;
+       int     temp;
+       int     v_batt;
+       int     sense_d;
+       int     sense_m;
+
+       int     acceleration;
+       int     speed;
+       int     height;
+
+       int     ground_accel;
+       int     ground_pres;
+       int     accel_plus_g;
+       int     accel_minus_g;
+
+       int     rssi;
+
+       public AltosTelemetryRecordSensor(int[] in_bytes, int in_rssi) {
+               super(in_bytes);
+               state         = uint8(5);
+
+               accel         = int16(6);
+               pres          = int16(8);
+               temp          = int16(10);
+               v_batt        = int16(12);
+               sense_d       = int16(14);
+               sense_m       = int16(16);
+
+               acceleration  = int16(18);
+               speed         = int16(20);
+               height        = int16(22);
+
+               ground_pres   = int16(24);
+               ground_accel  = int16(26);
+               accel_plus_g  = int16(28);
+               accel_minus_g = int16(30);
+
+               rssi          = in_rssi;
+       }
+
+       public AltosRecord update_state(AltosRecord prev) {
+               AltosRecord     n = super.update_state(prev);
+
+               AltosRecordTM   next;
+               if (!(n instanceof AltosRecordTM))
+                       next = new AltosRecordTM(n);
+               else
+                       next = (AltosRecordTM) n;
+
+               next.state = state;
+               if (type == packet_type_TM_sensor)
+                       next.accel = accel;
+               else
+                       next.accel = AltosRecord.MISSING;
+               next.pres = pres;
+               next.temp = temp;
+               next.batt = v_batt;
+               if (type == packet_type_TM_sensor || type == packet_type_Tm_sensor) {
+                       next.drogue = sense_d;
+                       next.main = sense_m;
+               } else {
+                       next.drogue = AltosRecord.MISSING;
+                       next.main = AltosRecord.MISSING;
+               }
+
+               next.acceleration = acceleration / 16.0;
+               next.speed = speed / 16.0;
+               next.height = height;
+
+               next.ground_pres = ground_pres;
+               if (type == packet_type_TM_sensor) {
+                       next.ground_accel = ground_accel;
+                       next.accel_plus_g = accel_plus_g;
+                       next.accel_minus_g = accel_minus_g;
+               } else {
+                       next.ground_accel = AltosRecord.MISSING;
+                       next.accel_plus_g = AltosRecord.MISSING;
+                       next.accel_minus_g = AltosRecord.MISSING;
+               }
+
+               next.rssi = rssi;
+
+               next.seen |= AltosRecord.seen_sensor | AltosRecord.seen_temp_volt;
+
+               return next;
+       }
+}
diff --git a/altoslib/AltosUnits.java b/altoslib/AltosUnits.java
new file mode 100644 (file)
index 0000000..47540c6
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2012 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 org.altusmetrum.AltosLib;
+
+public abstract class AltosUnits {
+
+       public abstract double value(double v);
+
+       public abstract String show_units();
+
+       public abstract String say_units();
+
+       abstract int show_fraction(int width);
+
+       int say_fraction() {
+               return 0;
+       }
+
+       private String show_format(int width) {
+               return String.format("%%%d.%df %s", width, show_fraction(width), show_units());
+       }
+
+       private String say_format() {
+               return String.format("%%1.%df", say_fraction());
+       }
+
+       private String say_units_format() {
+               return String.format("%%1.%df %s", say_fraction(), say_units());
+       }
+
+       public String show(int width, double v) {
+               return String.format(show_format(width), value(v));
+       }
+
+       public String say(double v) {
+               return String.format(say_format(), value(v));
+       }
+
+       public String say_units(double v) {
+               return String.format(say_units_format(), value(v));
+       }
+}
\ No newline at end of file
diff --git a/altoslib/Makefile.am b/altoslib/Makefile.am
new file mode 100644 (file)
index 0000000..a9f810f
--- /dev/null
@@ -0,0 +1,95 @@
+AM_JAVACFLAGS=-encoding UTF-8 -Xlint:deprecation
+
+JAVAROOT=bin
+
+CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH="bin:$(FREETTS)/*:/usr/share/java/*"
+
+SRC=.
+BIN=bin/org/altusmetrum/AltosLib
+
+AltosLibdir = $(datadir)/java
+
+AltosLib_JAVA = \
+       $(SRC)/AltosLib.java \
+       $(SRC)/AltosConfigData.java \
+       $(SRC)/AltosConvert.java \
+       $(SRC)/AltosCRCException.java \
+       $(SRC)/AltosEepromChunk.java \
+       $(SRC)/AltosEepromIterable.java \
+       $(SRC)/AltosEepromLog.java \
+       $(SRC)/AltosEepromMega.java \
+       $(SRC)/AltosEepromMegaIterable.java \
+       $(SRC)/AltosEepromRecord.java \
+       $(SRC)/AltosEepromTeleScience.java \
+       $(SRC)/AltosFile.java \
+       $(SRC)/AltosFlightReader.java \
+       $(SRC)/AltosFrequency.java \
+       $(SRC)/AltosGPS.java \
+       $(SRC)/AltosGPSQuery.java \
+       $(SRC)/AltosGPSSat.java \
+       $(SRC)/AltosGreatCircle.java \
+       $(SRC)/AltosIdleMonitor.java \
+       $(SRC)/AltosIdleMonitorListener.java \
+       $(SRC)/AltosIgnite.java \
+       $(SRC)/AltosIMU.java \
+       $(SRC)/AltosIMUQuery.java \
+       $(SRC)/AltosLine.java \
+       $(SRC)/AltosLink.java \
+       $(SRC)/AltosLog.java \
+       $(SRC)/AltosMs5607.java \
+       $(SRC)/AltosMs5607Query.java \
+       $(SRC)/AltosOrderedRecord.java \
+       $(SRC)/AltosOrderedMegaRecord.java \
+       $(SRC)/AltosParse.java \
+       $(SRC)/AltosPreferences.java \
+       $(SRC)/AltosRecordCompanion.java \
+       $(SRC)/AltosRecordIterable.java \
+       $(SRC)/AltosRecord.java \
+       $(SRC)/AltosRecordTM.java \
+       $(SRC)/AltosRecordMM.java \
+       $(SRC)/AltosReplayReader.java \
+       $(SRC)/AltosSensorMM.java \
+       $(SRC)/AltosSensorTM.java \
+       $(SRC)/AltosState.java \
+       $(SRC)/AltosTelemetry.java \
+       $(SRC)/AltosTelemetryIterable.java \
+       $(SRC)/AltosTelemetryMap.java \
+       $(SRC)/AltosTelemetryReader.java \
+       $(SRC)/AltosTelemetryRecordCompanion.java \
+       $(SRC)/AltosTelemetryRecordConfiguration.java \
+       $(SRC)/AltosTelemetryRecordGeneral.java \
+       $(SRC)/AltosTelemetryRecord.java \
+       $(SRC)/AltosTelemetryRecordLegacy.java \
+       $(SRC)/AltosTelemetryRecordLocation.java \
+       $(SRC)/AltosTelemetryRecordRaw.java \
+       $(SRC)/AltosTelemetryRecordSatellite.java \
+       $(SRC)/AltosTelemetryRecordSensor.java \
+       $(SRC)/AltosTelemetryRecordMegaSensor.java \
+       $(SRC)/AltosTelemetryRecordMegaData.java \
+       $(SRC)/AltosMs5607.java \
+       $(SRC)/AltosIMU.java \
+       $(SRC)/AltosMag.java \
+       $(SRC)/AltosUnits.java \
+       $(SRC)/AltosDistance.java \
+       $(SRC)/AltosHeight.java \
+       $(SRC)/AltosSpeed.java \
+       $(SRC)/AltosAccel.java
+
+JAR=AltosLib.jar
+
+all-local: $(JAR)
+
+clean-local:
+       -rm -rf bin $(JAR)
+
+install-AltosLibJAVA: $(JAR)
+       @$(NORMAL_INSTALL)
+       test -z "$(AltosLibdir)" || $(MKDIR_P) "$(DESTDIR)$(AltosLibdir)"
+       echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(AltosLibdir)/$(JAR)"; \
+       $(INSTALL_DATA) "$<" "$(DESTDIR)$(AltosLibdir)"
+
+bin:
+       mkdir -p bin
+
+$(JAR): classAltosLib.stamp
+       jar cf $@ -C bin org
diff --git a/altosui/.gitignore b/altosui/.gitignore
new file mode 100644 (file)
index 0000000..6d2d8c2
--- /dev/null
@@ -0,0 +1,22 @@
+windows/
+linux/
+macosx/
+fat/
+Manifest.txt
+Manifest-fat.txt
+AltosVersion.java
+Info.plist
+libaltosJNI
+classes
+altosui
+altosui-test
+altosui-jdb
+classaltosui.stamp
+Altos-Linux-*.tar.bz2
+Altos-Mac-*.zip
+Altos-Windows-*.exe
+*.dll
+*.dylib
+*.so
+*.jar
+*.class
diff --git a/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml b/altosui/AltOS Package Configuration.pmdoc/01altosui-contents.xml
new file mode 100644 (file)
index 0000000..18e00fe
--- /dev/null
@@ -0,0 +1 @@
+<pkg-contents spec="1.12"><f n="AltosUI.app" o="keithp" g="keithp" p="16877" pt="/Users/keithp/altos/ao-tools/altosui/AltosUI.app" m="false" t="file"><f n="Contents" o="keithp" g="keithp" p="16877"><f n="Info.plist" o="keithp" g="keithp" p="33188"/><f n="MacOS" o="keithp" g="keithp" p="16877"><f n="JavaApplicationStub" o="keithp" g="keithp" p="33133"/></f><f n="PkgInfo" o="keithp" g="keithp" p="33188"/><f n="Resources" o="keithp" g="keithp" p="16877"><f n="AltosUIIcon.icns" o="keithp" g="keithp" p="33188"/><f n="Java" o="keithp" g="keithp" p="16877"/></f></f></f></pkg-contents>
\ No newline at end of file
diff --git a/altosui/AltOS Package Configuration.pmdoc/01altosui.xml b/altosui/AltOS Package Configuration.pmdoc/01altosui.xml
new file mode 100644 (file)
index 0000000..6170931
--- /dev/null
@@ -0,0 +1 @@
+<pkgref spec="1.12" uuid="C5762664-2F26-4536-94C4-56F0FBC08D1A"><config><identifier>org.altusmetrum.altosUi.AltosUI.pkg</identifier><version>0.7</version><description></description><post-install type="none"/><installFrom relative="true" mod="true">AltosUI.app</installFrom><installTo mod="true" relocatable="true">/Applications/AltosUI.app</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>installTo.path</mod><mod>installFrom.isRelativeType</mod><mod>version</mod><mod>parent</mod><mod>requireAuthorization</mod><mod>installTo</mod></config><contents><file-list>01altosui-contents.xml</file-list><filter>/CVS$</filter><filter>/\.svn$</filter><filter>/\.cvsignore$</filter><filter>/\.cvspass$</filter><filter>/\.DS_Store$</filter></contents></pkgref>
\ No newline at end of file
diff --git a/altosui/AltOS Package Configuration.pmdoc/index.xml b/altosui/AltOS Package Configuration.pmdoc/index.xml
new file mode 100644 (file)
index 0000000..fabe54a
--- /dev/null
@@ -0,0 +1 @@
+<pkmkdoc spec="1.12"><properties><title>AltOS UI</title><build>/Users/keithp/altos/ao-tools/altosui/AltosUI.pkg</build><organization>org.altusmetrum</organization><userSees ui="both"/><min-target os="3"/><domain system="true" user="true"/></properties><distribution><versions min-spec="1.000000"/><scripts></scripts></distribution><description>Install AltOS User Interface</description><contents><choice title="AltosUI" id="choice0" starts_selected="true" starts_enabled="true" starts_hidden="false"><pkgref id="org.altusmetrum.altosUi.AltosUI.pkg"/></choice></contents><resources bg-scale="tofit" bg-align="center"><locale lang="en"><resource relative="true" mod="true" type="background">altusmetrum.jpg</resource></locale></resources><flags/><item type="file">01altosui.xml</item><mod>properties.anywhereDomain</mod><mod>properties.title</mod><mod>properties.customizeOption</mod><mod>description</mod><mod>properties.userDomain</mod><mod>properties.systemDomain</mod></pkmkdoc>
\ No newline at end of file
diff --git a/altosui/Altos.java b/altosui/Altos.java
new file mode 100644 (file)
index 0000000..cd17a93
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * 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.util.*;
+import java.text.*;
+import java.nio.charset.Charset;
+
+import libaltosJNI.*;
+
+import org.altusmetrum.AltosLib.*;
+
+public class Altos extends AltosLib {
+
+       static final int tab_elt_pad = 5;
+
+       static Font label_font;
+       static Font value_font;
+       static Font status_font;
+       static Font table_label_font;
+       static Font table_value_font;
+
+       final static int font_size_small = 1;
+       final static int font_size_medium = 2;
+       final static int font_size_large = 3;
+
+       static void set_fonts(int size) {
+               int     brief_size;
+               int     table_size;
+               int     status_size;
+
+               switch (size) {
+               case font_size_small:
+                       brief_size = 16;
+                       status_size = 18;
+                       table_size = 11;
+                       break;
+               default:
+               case font_size_medium:
+                       brief_size = 22;
+                       status_size = 24;
+                       table_size = 14;
+                       break;
+               case font_size_large:
+                       brief_size = 26;
+                       status_size = 30;
+                       table_size = 17;
+                       break;
+               }
+               label_font = new Font("Dialog", Font.PLAIN, brief_size);
+               value_font = new Font("Monospaced", Font.PLAIN, brief_size);
+               status_font = new Font("SansSerif", Font.BOLD, status_size);
+               table_label_font = new Font("SansSerif", Font.PLAIN, table_size);
+               table_value_font = new Font("Monospaced", Font.PLAIN, table_size);
+       }
+
+       static final int text_width = 20;
+
+       static public boolean initialized = false;
+       static public boolean loaded_library = false;
+
+       public static boolean load_library() {
+               if (!initialized) {
+                       try {
+                               System.loadLibrary("altos");
+                               libaltos.altos_init();
+                               loaded_library = true;
+                       } catch (UnsatisfiedLinkError e) {
+                               try {
+                                       System.loadLibrary("altos64");
+                                       libaltos.altos_init();
+                                       loaded_library = true;
+                               } catch (UnsatisfiedLinkError e2) {
+                                       loaded_library = false;
+                               }
+                       }
+                       initialized = true;
+               }
+               return loaded_library;
+       }
+}
diff --git a/altosui/AltosAscent.java b/altosui/AltosAscent.java
new file mode 100644 (file)
index 0000000..a158eb2
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosAscent extends JComponent implements AltosFlightDisplay {
+       GridBagLayout   layout;
+       JLabel                  cur, max;
+
+       public class AscentStatus {
+               JLabel          label;
+               JTextField      value;
+               AltosLights     lights;
+
+               void show() {
+                       value.setVisible(true);
+                       lights.setVisible(true);
+                       label.setVisible(true);
+               }
+
+               void hide() {
+                       value.setVisible(false);
+                       lights.setVisible(false);
+                       label.setVisible(false);
+               }
+
+               void show(AltosState state, int crc_errors) {}
+               void reset() {
+                       value.setText("");
+                       lights.set(false);
+               }
+
+               void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+               }
+
+               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("");
+               }
+
+               void show() {
+                       label.setVisible(true);
+                       value.setVisible(true);
+               }
+
+               void hide() {
+                       label.setVisible(false);
+                       value.setVisible(false);
+               }
+               void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+               }
+
+               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 = AltosRecord.MISSING;
+               }
+
+               void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+                       max_value.setFont(Altos.value_font);
+               }
+
+               void show(AltosUnits units, double v) {
+                       if (v == AltosRecord.MISSING) {
+                               value.setText("Missing");
+                               max_value.setText("Missing");
+                       } else {
+                               value.setText(units.show(8, v));
+                               if (v > max || max == AltosRecord.MISSING) {
+                                       max_value.setText(units.show(8, 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(AltosConvert.height, 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(AltosConvert.speed, 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(AltosConvert.accel, 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) {
+                       show();
+                       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) {
+                       show();
+                       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) {
+                       show();
+                       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) {
+                       show();
+                       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 set_font() {
+               cur.setFont(Altos.label_font);
+               max.setFont(Altos.label_font);
+               lat.set_font();
+               lon.set_font();
+               main.set_font();
+               apogee.set_font();
+               height.set_font();
+               speed.set_font();
+               accel.set_font();
+       }
+
+       public void show(AltosState state, int crc_errors) {
+               if (state.gps != null && state.gps.connected) {
+                       lat.show(state, crc_errors);
+                       lon.show(state, crc_errors);
+               } else {
+                       lat.hide();
+                       lon.hide();
+               }
+               height.show(state, crc_errors);
+               if (state.main_sense != AltosRecord.MISSING)
+                       main.show(state, crc_errors);
+               else
+                       main.hide();
+               if (state.drogue_sense != AltosRecord.MISSING)
+                       apogee.show(state, crc_errors);
+               else
+                       apogee.hide();
+               speed.show(state, crc_errors);
+               accel.show(state, crc_errors);
+       }
+
+       public void labels(GridBagLayout layout, int y) {
+               GridBagConstraints      c;
+
+               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/altosui/AltosBTDevice.java b/altosui/AltosBTDevice.java
new file mode 100644 (file)
index 0000000..5e353fd
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright © 2011 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.util.*;
+import libaltosJNI.*;
+
+public class AltosBTDevice extends altos_bt_device implements AltosDevice {
+
+       public String getProductName() {
+               String  name = getName();
+               if (name == null)
+                       return "Altus Metrum";
+               int     dash = name.lastIndexOf("-");
+               if (dash < 0)
+                       return name;
+               return name.substring(0,dash);
+       }
+
+       public int getProduct() {
+               if (Altos.bt_product_telebt.equals(getProductName()))
+                       return Altos.product_telebt;
+               return 0;
+       }
+
+       public String getPath() {
+               return getAddr();
+       }
+
+       public String getErrorString() {
+               altos_error     error = new altos_error();
+
+               libaltos.altos_get_last_error(error);
+               return String.format("%s (%d)", error.getString(), error.getCode());
+       }
+
+       public int getSerial() {
+               String name = getName();
+               if (name == null)
+                       return 0;
+               int dash = name.lastIndexOf("-");
+               if (dash < 0 || dash >= name.length())
+                       return 0;
+               String sn = name.substring(dash + 1, name.length());
+               try {
+                       return Integer.parseInt(sn);
+               } catch (NumberFormatException ne) {
+                       return 0;
+               }
+       }
+
+       public String toString() {
+               return String.format("%-20.20s %4d %s",
+                                    getProductName(), getSerial(), getAddr());
+       }
+
+       public String toShortString() {
+               return String.format("%s %d %s",
+                                    getProductName(), getSerial(), getAddr());
+
+       }
+
+       public SWIGTYPE_p_altos_file open() {
+               return libaltos.altos_bt_open(this);
+       }
+
+       private boolean isAltusMetrum() {
+               if (getName().startsWith(Altos.bt_product_telebt))
+                       return true;
+               return false;
+       }
+
+       public boolean matchProduct(int want_product) {
+
+//             if (!isAltusMetrum())
+//                     return false;
+
+               if (want_product == Altos.product_any)
+                       return true;
+
+               if (want_product == Altos.product_basestation)
+                       return matchProduct(Altos.product_telebt);
+
+               if (want_product == getProduct())
+                       return true;
+
+               return false;
+       }
+
+       public boolean equals(Object o) {
+               if (!(o instanceof AltosBTDevice))
+                       return false;
+               AltosBTDevice other = (AltosBTDevice) o;
+               return getName().equals(other.getName()) && getAddr().equals(other.getAddr());
+       }
+
+       public int hashCode() {
+               return getName().hashCode() ^ getAddr().hashCode();
+       }
+
+       public AltosBTDevice(String name, String addr) {
+               Altos.load_library();
+               libaltos.altos_bt_fill_in(name, addr,this);
+       }
+
+       public AltosBTDevice() {
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosBTDeviceIterator.java b/altosui/AltosBTDeviceIterator.java
new file mode 100644 (file)
index 0000000..58ed86d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2011 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.util.*;
+import libaltosJNI.*;
+
+public class AltosBTDeviceIterator implements Iterator<AltosBTDevice> {
+       AltosBTDevice   current;
+       boolean         done;
+       SWIGTYPE_p_altos_bt_list list;
+
+       public boolean hasNext() {
+               if (list == null)
+                       return false;
+               if (current != null)
+                       return true;
+               if (done)
+                       return false;
+               current = new AltosBTDevice();
+               while (libaltos.altos_bt_list_next(list, current) != 0) {
+//                     if (current.matchProduct(product))
+                               return true;
+               }
+               current = null;
+               done = true;
+               return false;
+       }
+
+       public AltosBTDevice next() {
+               if (hasNext()) {
+                       AltosBTDevice   next = current;
+                       current = null;
+                       return next;
+               }
+               return null;
+       }
+
+       public void remove() {
+               throw new UnsupportedOperationException();
+       }
+
+       public AltosBTDeviceIterator(int inquiry_time) {
+               done = false;
+               current = null;
+               list = libaltos.altos_bt_list_start(inquiry_time);
+       }
+}
diff --git a/altosui/AltosBTKnown.java b/altosui/AltosBTKnown.java
new file mode 100644 (file)
index 0000000..6a8e53c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2011 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.util.*;
+import libaltosJNI.*;
+import java.util.prefs.*;
+
+public class AltosBTKnown implements Iterable<AltosBTDevice> {
+       LinkedList<AltosBTDevice>       devices = new LinkedList<AltosBTDevice>();
+       Preferences                     bt_pref = AltosUIPreferences.bt_devices();
+
+       private String get_address(String name) {
+               return bt_pref.get(name, "");
+       }
+
+       private void set_address(String name, String addr) {
+               bt_pref.put(name, addr);
+       }
+
+       private void remove(String name) {
+               bt_pref.remove(name);
+       }
+
+       private void load() {
+               try {
+                       String[] names = bt_pref.keys();
+                       for (int i = 0; i < names.length; i++) {
+                               String  name = names[i];
+                               String  addr = get_address(name);
+                               devices.add(new AltosBTDevice(name, addr));
+                       }
+               } catch (BackingStoreException be) {
+               } catch (IllegalStateException ie) {
+               }
+       }
+
+       public Iterator<AltosBTDevice> iterator() {
+               return devices.iterator();
+       }
+
+       private void flush() {
+               AltosUIPreferences.flush_preferences();
+       }
+
+       public void set(Iterable<AltosBTDevice> new_devices) {
+               for (AltosBTDevice old : devices) {
+                       boolean found = false;
+                       for (AltosBTDevice new_device : new_devices) {
+                               if (new_device.equals(old)) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       if (!found)
+                               remove(old.getName());
+               }
+               devices = new LinkedList<AltosBTDevice>();
+               for (AltosBTDevice new_device : new_devices) {
+                       devices.add(new_device);
+                       set_address(new_device.getName(), new_device.getAddr());
+               }
+               flush();
+       }
+
+       public List<AltosDevice> list(int product) {
+               LinkedList<AltosDevice> list = new LinkedList<AltosDevice>();
+               for (AltosBTDevice device : devices) {
+                       if (device.matchProduct(product))
+                               list.add(device);
+               }
+               return list;
+       }
+
+       public AltosBTKnown() {
+               devices = new LinkedList<AltosBTDevice>();
+               bt_pref = AltosUIPreferences.bt_devices();
+               load();
+       }
+
+       static AltosBTKnown     known;
+
+       static public AltosBTKnown bt_known() {
+               if (known == null)
+                       known = new AltosBTKnown();
+               return known;
+       }
+}
diff --git a/altosui/AltosBTManage.java b/altosui/AltosBTManage.java
new file mode 100644 (file)
index 0000000..aeb964b
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * Copyright © 2011 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 javax.swing.plaf.basic.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.util.prefs.*;
+import java.util.concurrent.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosBTManage extends AltosDialog implements ActionListener, Iterable<AltosBTDevice> {
+       LinkedBlockingQueue<AltosBTDevice> found_devices;
+       Frame frame;
+       LinkedList<ActionListener> listeners;
+       AltosBTKnown    bt_known;
+
+       class DeviceList extends JList implements Iterable<AltosBTDevice> {
+               LinkedList<AltosBTDevice> devices;
+               DefaultListModel        list_model;
+
+               public void add (AltosBTDevice device) {
+                       if (!devices.contains(device)) {
+                               devices.add(device);
+                               list_model.addElement(device);
+                       }
+               }
+
+               public void remove (AltosBTDevice device) {
+                       if (devices.contains(device)) {
+                               devices.remove(device);
+                               list_model.removeElement(device);
+                       }
+               }
+
+               public boolean contains(AltosBTDevice device) {
+                       return devices.contains(device);
+               }
+
+               //Subclass JList to workaround bug 4832765, which can cause the
+               //scroll pane to not let the user easily scroll up to the beginning
+               //of the list.  An alternative would be to set the unitIncrement
+               //of the JScrollBar to a fixed value. You wouldn't get the nice
+               //aligned scrolling, but it should work.
+               public int getScrollableUnitIncrement(Rectangle visibleRect,
+                                                     int orientation,
+                                                     int direction) {
+                       int row;
+                       if (orientation == SwingConstants.VERTICAL &&
+                           direction < 0 && (row = getFirstVisibleIndex()) != -1) {
+                               Rectangle r = getCellBounds(row, row);
+                               if ((r.y == visibleRect.y) && (row != 0))  {
+                                       Point loc = r.getLocation();
+                                       loc.y--;
+                                       int prevIndex = locationToIndex(loc);
+                                       Rectangle prevR = getCellBounds(prevIndex, prevIndex);
+
+                                       if (prevR == null || prevR.y >= r.y) {
+                                               return 0;
+                                       }
+                                       return prevR.height;
+                               }
+                       }
+                       return super.getScrollableUnitIncrement(
+                               visibleRect, orientation, direction);
+               }
+
+               public Iterator<AltosBTDevice> iterator() {
+                       return devices.iterator();
+               }
+
+               public java.util.List<AltosBTDevice> selected_list() {
+                       java.util.LinkedList<AltosBTDevice> l = new java.util.LinkedList<AltosBTDevice>();
+                       Object[] a = getSelectedValues();
+                       for (int i = 0; i < a.length; i++)
+                               l.add((AltosBTDevice)a[i]);
+                       return l;
+               }
+
+               public DeviceList() {
+                       devices = new LinkedList<AltosBTDevice>();
+                       list_model = new DefaultListModel();
+                       setModel(list_model);
+                       setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+                       setLayoutOrientation(JList.HORIZONTAL_WRAP);
+                       setVisibleRowCount(-1);
+               }
+       }
+
+       DeviceList      visible_devices;
+
+       DeviceList      known_devices;
+       Thread          bt_thread;
+
+       public Iterator<AltosBTDevice> iterator() {
+               return known_devices.iterator();
+       }
+
+       public void commit() {
+               bt_known.set(this);
+       }
+
+       public void add_known() {
+               for (AltosBTDevice device : visible_devices.selected_list()) {
+                       known_devices.add(device);
+                       visible_devices.remove(device);
+               }
+       }
+
+       public void remove_known() {
+               for (AltosBTDevice device : known_devices.selected_list()) {
+                       known_devices.remove(device);
+                       visible_devices.add(device);
+               }
+       }
+
+       public void addActionListener(ActionListener l) {
+               listeners.add(l);
+       }
+
+       private void forwardAction(ActionEvent e) {
+               for (ActionListener l : listeners)
+                       l.actionPerformed(e);
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String  command = e.getActionCommand();
+               if ("ok".equals(command)) {
+                       bt_thread.interrupt();
+                       commit();
+                       setVisible(false);
+                       forwardAction(e);
+               } else if ("cancel".equals(command)) {
+                       bt_thread.interrupt();
+                       setVisible(false);
+                       forwardAction(e);
+               } else if ("select".equals(command)) {
+                       add_known();
+               } else if ("deselect".equals(command)) {
+                       remove_known();
+               }
+       }
+
+       public void got_visible_device() {
+               while (!found_devices.isEmpty()) {
+                       AltosBTDevice   device = found_devices.remove();
+                       if (!known_devices.contains(device))
+                               visible_devices.add(device);
+               }
+       }
+
+       class BTGetVisibleDevices implements Runnable {
+               public void run () {
+                       for (;;)
+                               for (int time = 1; time <= 8; time <<= 1) {
+                                       AltosBTDeviceIterator   i = new AltosBTDeviceIterator(time);
+                                       AltosBTDevice           device;
+
+                                       if (Thread.interrupted())
+                                               return;
+                                       try {
+                                               while ((device = i.next()) != null) {
+                                                       Runnable r;
+
+                                                       if (Thread.interrupted())
+                                                               return;
+                                                       found_devices.add(device);
+                                                       r = new Runnable() {
+                                                                       public void run() {
+                                                                               got_visible_device();
+                                                                       }
+                                                               };
+                                                       SwingUtilities.invokeLater(r);
+                                               }
+                                       } catch (Exception e) {
+                                               System.out.printf("uh-oh, exception %s\n", e.toString());
+                                       }
+                               }
+               }
+       }
+
+       public static void show(Component frameComp, AltosBTKnown known) {
+               Frame   frame = JOptionPane.getFrameForComponent(frameComp);
+               AltosBTManage   dialog;
+
+               dialog = new AltosBTManage(frame, known);
+               dialog.setVisible(true);
+       }
+
+       public AltosBTManage(Frame in_frame, AltosBTKnown in_known) {
+               super(in_frame, "Manage Bluetooth Devices", true);
+
+               frame = in_frame;
+               bt_known = in_known;
+               BTGetVisibleDevices     get_visible_devices = new BTGetVisibleDevices();
+               bt_thread = new Thread(get_visible_devices);
+               bt_thread.start();
+
+               listeners = new LinkedList<ActionListener>();
+
+               found_devices = new LinkedBlockingQueue<AltosBTDevice>();
+
+               Container pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               GridBagConstraints c = new GridBagConstraints();
+               c.insets = new Insets(4,4,4,4);
+
+               /*
+                * Known devices label and list
+                */
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(new JLabel("Known Devices"), c);
+
+               known_devices = new DeviceList();
+               for (AltosBTDevice device : bt_known)
+                       known_devices.add(device);
+
+               JScrollPane known_list_scroller = new JScrollPane(known_devices);
+               known_list_scroller.setPreferredSize(new Dimension(400, 80));
+               known_list_scroller.setAlignmentX(LEFT_ALIGNMENT);
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 0;
+               c.gridy = 1;
+               c.gridwidth = 1;
+               c.gridheight = 2;
+               c.weightx = 1;
+               c.weighty = 1;
+               pane.add(known_list_scroller, c);
+
+               /*
+                * Visible devices label and list
+                */
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 2;
+               c.gridy = 0;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+
+               pane.add(new JLabel("Visible Devices"), c);
+
+               visible_devices = new DeviceList();
+               JScrollPane visible_list_scroller = new JScrollPane(visible_devices);
+               visible_list_scroller.setPreferredSize(new Dimension(400, 80));
+               visible_list_scroller.setAlignmentX(LEFT_ALIGNMENT);
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 2;
+               c.gridy = 1;
+               c.gridheight = 2;
+               c.gridwidth = 1;
+               c.weightx = 1;
+               c.weighty = 1;
+               pane.add(visible_list_scroller, c);
+
+               /*
+                * Arrows between the two lists
+                */
+               BasicArrowButton select_arrow = new BasicArrowButton(SwingConstants.WEST);
+               select_arrow.setActionCommand("select");
+               select_arrow.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.SOUTH;
+               c.gridx = 1;
+               c.gridy = 1;
+               c.gridheight = 1;
+               c.gridwidth = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(select_arrow, c);
+
+               BasicArrowButton deselect_arrow = new BasicArrowButton(SwingConstants.EAST);
+               deselect_arrow.setActionCommand("deselect");
+               deselect_arrow.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.NORTH;
+               c.gridx = 1;
+               c.gridy = 2;
+               c.gridheight = 1;
+               c.gridwidth = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(deselect_arrow, c);
+
+               JButton cancel_button = new JButton("Cancel");
+               cancel_button.setActionCommand("cancel");
+               cancel_button.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0;
+               c.gridy = 3;
+               c.gridheight = 1;
+               c.gridwidth = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(cancel_button, c);
+
+               JButton ok_button = new JButton("OK");
+               ok_button.setActionCommand("ok");
+               ok_button.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 2;
+               c.gridy = 3;
+               c.gridheight = 1;
+               c.gridwidth = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(ok_button, c);
+
+               getRootPane().setDefaultButton(ok_button);
+
+               pack();
+               setLocationRelativeTo(frame);
+               setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+               addWindowListener(new WindowAdapter() {
+                       @Override
+                       public void windowClosing(WindowEvent e) {
+                               bt_thread.interrupt();
+                               setVisible(false);
+                       }
+               });
+       }
+}
diff --git a/altosui/AltosCSV.java b/altosui/AltosCSV.java
new file mode 100644 (file)
index 0000000..c876d9c
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * 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.io.*;
+import java.text.*;
+import java.util.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosCSV implements AltosWriter {
+       File                    name;
+       PrintStream             out;
+       boolean                 header_written;
+       boolean                 seen_boost;
+       int                     boost_tick;
+       LinkedList<AltosRecord> pad_records;
+       AltosState              state;
+
+       static final int ALTOS_CSV_VERSION = 5;
+
+       /* Version 4 format:
+        *
+        * General info
+        *      version number
+        *      serial number
+        *      flight number
+        *      callsign
+        *      time (seconds since boost)
+        *      clock (tick count / 100)
+        *      rssi
+        *      link quality
+        *
+        * Flight status
+        *      state
+        *      state name
+        *
+        * Basic sensors
+        *      acceleration (m/s²)
+        *      pressure (mBar)
+        *      altitude (m)
+        *      height (m)
+        *      accelerometer speed (m/s)
+        *      barometer speed (m/s)
+        *      temp (°C)
+        *      battery (V)
+        *      drogue (V)
+        *      main (V)
+        *
+        * Advanced sensors (if available)
+        *      accel_x (m/s²)
+        *      accel_y (m/s²)
+        *      accel_z (m/s²)
+        *      gyro_x (d/s)
+        *      gyro_y (d/s)
+        *      gyro_z (d/s)
+        *      mag_x (g)
+        *      mag_y (g)
+        *      mag_z (g)
+        *
+        * GPS data (if available)
+        *      connected (1/0)
+        *      locked (1/0)
+        *      nsat (used for solution)
+        *      latitude (°)
+        *      longitude (°)
+        *      altitude (m)
+        *      year (e.g. 2010)
+        *      month (1-12)
+        *      day (1-31)
+        *      hour (0-23)
+        *      minute (0-59)
+        *      second (0-59)
+        *      from_pad_dist (m)
+        *      from_pad_azimuth (deg true)
+        *      from_pad_range (m)
+        *      from_pad_elevation (deg from horizon)
+        *      hdop
+        *
+        * GPS Sat data
+        *      C/N0 data for all 32 valid SDIDs
+        *
+        * Companion data
+        *      companion_id (1-255. 10 is TeleScience)
+        *      time of last companion data (seconds since boost)
+        *      update_period (0.1-2.55 minimum telemetry interval)
+        *      channels (0-12)
+        *      channel data for all 12 possible channels
+        */
+
+       void write_general_header() {
+               out.printf("version,serial,flight,call,time,clock,rssi,lqi");
+       }
+
+       void write_general(AltosRecord record) {
+               out.printf("%s, %d, %d, %s, %8.2f, %8.2f, %4d, %3d",
+                          ALTOS_CSV_VERSION, record.serial, record.flight, record.callsign,
+                          (double) record.time, (double) record.tick / 100.0,
+                          record.rssi,
+                          record.status & 0x7f);
+       }
+
+       void write_flight_header() {
+               out.printf("state,state_name");
+       }
+
+       void write_flight(AltosRecord record) {
+               out.printf("%d,%8s", record.state, record.state());
+       }
+
+       void write_basic_header() {
+               out.printf("acceleration,pressure,altitude,height,accel_speed,baro_speed,temperature,battery_voltage,drogue_voltage,main_voltage");
+       }
+
+       void write_basic(AltosRecord record) {
+               out.printf("%8.2f,%10.2f,%8.2f,%8.2f,%8.2f,%8.2f,%5.1f,%5.2f,%5.2f,%5.2f",
+                          record.acceleration(),
+                          record.raw_pressure(),
+                          record.raw_altitude(),
+                          record.raw_height(),
+                          record.accel_speed(),
+                          state.baro_speed,
+                          record.temperature(),
+                          record.battery_voltage(),
+                          record.drogue_voltage(),
+                          record.main_voltage());
+       }
+
+       void write_advanced_header() {
+               out.printf("accel_x,accel_y,accel_z,gyro_x,gyro_y,gyro_z");
+       }
+
+       void write_advanced(AltosRecord record) {
+               AltosIMU        imu = record.imu();
+               AltosMag        mag = record.mag();
+
+               if (imu == null)
+                       imu = new AltosIMU();
+               if (mag == null)
+                       mag = new AltosMag();
+               out.printf("%6d,%6d,%6d,%6d,%6d,%6d,%6d,%6d,%6d",
+                          imu.accel_x, imu.accel_y, imu.accel_z,
+                          imu.gyro_x, imu.gyro_y, imu.gyro_z,
+                          mag.x, mag.y, mag.z);
+       }
+
+       void write_gps_header() {
+               out.printf("connected,locked,nsat,latitude,longitude,altitude,year,month,day,hour,minute,second,pad_dist,pad_range,pad_az,pad_el,hdop");
+       }
+
+       void write_gps(AltosRecord record) {
+               AltosGPS        gps = record.gps;
+               if (gps == null)
+                       gps = new AltosGPS();
+
+               AltosGreatCircle from_pad = state.from_pad;
+               if (from_pad == null)
+                       from_pad = new AltosGreatCircle();
+
+               out.printf("%2d,%2d,%3d,%12.7f,%12.7f,%6d,%5d,%3d,%3d,%3d,%3d,%3d,%9.0f,%9.0f,%4.0f,%4.0f,%6.1f",
+                          gps.connected?1:0,
+                          gps.locked?1:0,
+                          gps.nsat,
+                          gps.lat,
+                          gps.lon,
+                          gps.alt,
+                          gps.year,
+                          gps.month,
+                          gps.day,
+                          gps.hour,
+                          gps.minute,
+                          gps.second,
+                          from_pad.distance,
+                          state.range,
+                          from_pad.bearing,
+                          state.elevation,
+                          gps.hdop);
+       }
+
+       void write_gps_sat_header() {
+               for(int i = 1; i <= 32; i++) {
+                       out.printf("sat%02d", i);
+                       if (i != 32)
+                               out.printf(",");
+               }
+       }
+
+       void write_gps_sat(AltosRecord record) {
+               AltosGPS        gps = record.gps;
+               for(int i = 1; i <= 32; i++) {
+                       int     c_n0 = 0;
+                       if (gps != null && gps.cc_gps_sat != null) {
+                               for(int j = 0; j < gps.cc_gps_sat.length; j++)
+                                       if (gps.cc_gps_sat[j].svid == i) {
+                                               c_n0 = gps.cc_gps_sat[j].c_n0;
+                                               break;
+                                       }
+                       }
+                       out.printf ("%3d", c_n0);
+                       if (i != 32)
+                               out.printf(",");
+               }
+       }
+
+       void write_companion_header() {
+               out.printf("companion_id,companion_time,companion_update,companion_channels");
+               for (int i = 0; i < 12; i++)
+                       out.printf(",companion_%02d", i);
+       }
+
+       void write_companion(AltosRecord record) {
+               AltosRecordCompanion companion = record.companion;
+
+               int     channels_written = 0;
+               if (companion == null) {
+                       out.printf("0,0,0,0");
+               } else {
+                       out.printf("%3d,%5.2f,%5.2f,%2d",
+                                  companion.board_id,
+                                  (companion.tick - boost_tick) / 100.0,
+                                  companion.update_period / 100.0,
+                                  companion.channels);
+                       for (; channels_written < companion.channels; channels_written++)
+                               out.printf(",%5d", companion.companion_data[channels_written]);
+               }
+               for (; channels_written < 12; channels_written++)
+                       out.printf(",0");
+       }
+
+       void write_header(boolean advanced, boolean gps, boolean companion) {
+               out.printf("#"); write_general_header();
+               out.printf(","); write_flight_header();
+               out.printf(","); write_basic_header();
+               if (advanced)
+                       out.printf(","); write_advanced_header();
+               if (gps) {
+                       out.printf(","); write_gps_header();
+                       out.printf(","); write_gps_sat_header();
+               }
+               if (companion) {
+                       out.printf(","); write_companion_header();
+               }
+               out.printf ("\n");
+       }
+
+       void write_one(AltosRecord record) {
+               state = new AltosState(record, state);
+               write_general(record); out.printf(",");
+               write_flight(record); out.printf(",");
+               write_basic(record); out.printf(",");
+               if (record.imu() != null || record.mag() != null)
+                       write_advanced(record);
+               if (record.gps != null) {
+                       out.printf(",");
+                       write_gps(record); out.printf(",");
+                       write_gps_sat(record);
+               }
+               if (record.companion != null) {
+                       out.printf(",");
+                       write_companion(record);
+               }
+               out.printf ("\n");
+       }
+
+       void flush_pad() {
+               while (!pad_records.isEmpty()) {
+                       write_one (pad_records.remove());
+               }
+       }
+
+       public void write(AltosRecord record) {
+               if (record.state == Altos.ao_flight_startup)
+                       return;
+               if (!header_written) {
+                       write_header(record.imu() != null || record.mag() != null,
+                                    record.gps != null, record.companion != null);
+                       header_written = true;
+               }
+               if (!seen_boost) {
+                       if (record.state >= Altos.ao_flight_boost) {
+                               seen_boost = true;
+                               boost_tick = record.tick;
+                               flush_pad();
+                       }
+               }
+               if (seen_boost)
+                       write_one(record);
+               else
+                       pad_records.add(record);
+       }
+
+       public PrintStream out() {
+               return out;
+       }
+
+       public void close() {
+               if (!pad_records.isEmpty()) {
+                       boost_tick = pad_records.element().tick;
+                       flush_pad();
+               }
+               out.close();
+       }
+
+       public void write(AltosRecordIterable iterable) {
+               iterable.write_comments(out());
+               for (AltosRecord r : iterable)
+                       write(r);
+       }
+
+       public AltosCSV(PrintStream in_out, File in_name) {
+               name = in_name;
+               out = in_out;
+               pad_records = new LinkedList<AltosRecord>();
+       }
+
+       public AltosCSV(File in_name) throws FileNotFoundException {
+               this(new PrintStream(in_name), in_name);
+       }
+
+       public AltosCSV(String in_string) throws FileNotFoundException {
+               this(new File(in_string));
+       }
+}
diff --git a/altosui/AltosCSVUI.java b/altosui/AltosCSVUI.java
new file mode 100644 (file)
index 0000000..2702668
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosCSVUI
+       extends AltosDialog
+       implements ActionListener
+{
+       JFileChooser            csv_chooser;
+       JPanel                  accessory;
+       JComboBox               combo_box;
+       AltosRecordIterable     iterable;
+       AltosWriter             writer;
+
+       static String[]         combo_box_items = { "Comma Separated Values (.CSV)", "Googleearth Data (.KML)" };
+
+       void set_default_file() {
+               File    current = csv_chooser.getSelectedFile();
+               String  current_name = current.getName();
+               String  new_name = null;
+               String  selected = (String) combo_box.getSelectedItem();
+
+               if (selected.contains("CSV"))
+                       new_name = Altos.replace_extension(current_name, ".csv");
+               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 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);
+
+               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);
+
+               combo_box = new JComboBox(combo_box_items);
+               combo_box.addActionListener(this);
+               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();
+                       try {
+                               if (type.contains("CSV"))
+                                       writer = new AltosCSV(file);
+                               else
+                                       writer = new AltosKML(file);
+                               writer.write(iterable);
+                               writer.close();
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             ee.getMessage(),
+                                                             "Cannot open file",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+       }
+}
diff --git a/altosui/AltosChannelMenu.java b/altosui/AltosChannelMenu.java
new file mode 100644 (file)
index 0000000..0249a0b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosChannelMenu extends JComboBox implements ActionListener {
+       int                             channel;
+
+       public AltosChannelMenu(int current_channel) {
+
+               channel = current_channel;
+
+               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/altosui/AltosCompanionInfo.java b/altosui/AltosCompanionInfo.java
new file mode 100644 (file)
index 0000000..4ba8fe9
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosCompanionInfo extends JTable {
+       private AltosFlightInfoTableModel model;
+
+       static final int info_columns = 2;
+       static final int info_rows = 17;
+
+       int desired_row_height() {
+               FontMetrics     infoValueMetrics = getFontMetrics(Altos.table_value_font);
+               return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10;
+       }
+
+       public void set_font() {
+               setFont(Altos.table_value_font);
+               setRowHeight(desired_row_height());
+               doLayout();
+       }
+
+       public AltosCompanionInfo() {
+               super(new AltosFlightInfoTableModel(info_rows, info_columns));
+               model = (AltosFlightInfoTableModel) getModel();
+               setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS);
+               setShowGrid(true);
+               set_font();
+       }
+
+       public Dimension getPreferredScrollableViewportSize() {
+               return getPreferredSize();
+       }
+
+       void info_reset() {
+               model.reset();
+       }
+
+       void info_add_row(int col, String name, String value) {
+               model.addRow(col, name, value);
+       }
+
+       void info_add_row(int col, String name, String format, Object... parameters) {
+               info_add_row (col, name, String.format(format, parameters));
+       }
+
+       void info_finish() {
+               model.finish();
+       }
+
+       public void clear() {
+               model.clear();
+       }
+
+       AltosRecordCompanion    companion;
+
+       public String board_name() {
+               if (companion == null)
+                       return "None";
+               switch (companion.board_id) {
+               case AltosRecordCompanion.board_id_telescience:
+                       return "TeleScience";
+               default:
+                       return String.format("%02x\n", companion.board_id);
+               }
+       }
+       
+       public void show(AltosState state, int crc_errors) {
+               if (state == null)
+                       return;
+               if (state.data.companion != null)
+                       companion = state.data.companion;
+               info_reset();
+               info_add_row(0, "Companion board", "%s", board_name());
+               if (companion != null) {
+                       info_add_row(0, "Last Data", "%5d", companion.tick);
+                       info_add_row(0, "Update period", "%5.2f s",
+                                    companion.update_period / 100.0);
+                       info_add_row(0, "Channels", "%3d", companion.channels);
+
+                       for (int i = 0; i < companion.channels; i++)
+                               info_add_row(1, String.format("Channel %2d", i),
+                                            "%6d", companion.companion_data[i]);
+               }
+               info_finish();
+       }
+}
diff --git a/altosui/AltosConfig.java b/altosui/AltosConfig.java
new file mode 100644 (file)
index 0000000..cae4185
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosConfig implements ActionListener {
+
+       class int_ref {
+               int     value;
+
+               public int get() {
+                       return value;
+               }
+               public void set(int i) {
+                       value = i;
+               }
+               public int_ref(int i) {
+                       value = i;
+               }
+       }
+
+       class string_ref {
+               String  value;
+
+               public String get() {
+                       return value;
+               }
+               public void set(String i) {
+                       value = i;
+               }
+               public string_ref(String i) {
+                       value = i;
+               }
+       }
+
+       JFrame          owner;
+       AltosDevice     device;
+       AltosSerial     serial_line;
+       boolean         remote;
+       AltosConfigData remote_config_data;
+       double          remote_frequency;
+       int_ref         serial;
+       int_ref         log_format;
+       int_ref         main_deploy;
+       int_ref         apogee_delay;
+       int_ref         apogee_lockout;
+       int_ref         radio_channel;
+       int_ref         radio_calibration;
+       int_ref         flight_log_max;
+       int_ref         ignite_mode;
+       int_ref         pad_orientation;
+       int_ref         radio_setting;
+       int_ref         radio_frequency;
+       int_ref         storage_size;
+       int_ref         storage_erase_unit;
+       int_ref         stored_flight;
+       int_ref         radio_enable;
+       string_ref      version;
+       string_ref      product;
+       string_ref      callsign;
+       AltosConfigUI   config_ui;
+       boolean         serial_started;
+       boolean         made_visible;
+
+       boolean get_int(String line, String label, int_ref x) {
+               if (line.startsWith(label)) {
+                       try {
+                               String tail = line.substring(label.length()).trim();
+                               String[] tokens = tail.split("\\s+");
+                               if (tokens.length > 0) {
+                                       int     i = Integer.parseInt(tokens[0]);
+                                       x.set(i);
+                                       return true;
+                               }
+                       } catch (NumberFormatException ne) {
+                       }
+               }
+               return false;
+       }
+
+       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;
+               }
+       }
+
+       void start_serial() throws InterruptedException, TimeoutException {
+               serial_started = true;
+               if (remote)
+                       serial_line.start_remote();
+       }
+
+       void stop_serial() throws InterruptedException {
+               if (!serial_started)
+                       return;
+               serial_started = false;
+               if (remote)
+                       serial_line.stop_remote();
+       }
+
+       int log_limit() {
+               if (storage_size.get() > 0 && storage_erase_unit.get() > 0) {
+                       int     log_limit = storage_size.get() - storage_erase_unit.get();
+                       if (log_limit > 0)
+                               return log_limit / 1024;
+               }
+               return 1024;
+       }
+
+       void update_ui() {
+               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_apogee_lockout(apogee_lockout.get());
+               config_ui.set_radio_calibration(radio_calibration.get());
+               config_ui.set_radio_frequency(frequency());
+               boolean max_enabled = true;
+               switch (log_format.get()) {
+               case Altos.AO_LOG_FORMAT_TINY:
+                       max_enabled = false;
+                       break;
+               default:
+                       if (stored_flight.get() >= 0)
+                               max_enabled = false;
+                       break;
+               }
+               config_ui.set_flight_log_max_enabled(max_enabled);
+               config_ui.set_radio_enable(radio_enable.get());
+               config_ui.set_flight_log_max_limit(log_limit());
+               config_ui.set_flight_log_max(flight_log_max.get());
+               config_ui.set_ignite_mode(ignite_mode.get());
+               config_ui.set_pad_orientation(pad_orientation.get());
+               config_ui.set_callsign(callsign.get());
+               config_ui.set_clean();
+               if (!made_visible) {
+                       made_visible = true;
+                       config_ui.make_visible();
+               }
+       }
+
+       void process_line(String line) {
+               if (line == null) {
+                       abort();
+                       return;
+               }
+               if (line.equals("all finished")) {
+                       if (serial_line != null)
+                               update_ui();
+                       return;
+               }
+               get_int(line, "serial-number", serial);
+               get_int(line, "log-format", log_format);
+               get_int(line, "Main deploy:", main_deploy);
+               get_int(line, "Apogee delay:", apogee_delay);
+               get_int(line, "Apogee lockout:", apogee_lockout);
+               get_int(line, "Radio channel:", radio_channel);
+               get_int(line, "Radio cal:", radio_calibration);
+               get_int(line, "Max flight log:", flight_log_max);
+               get_int(line, "Ignite mode:", ignite_mode);
+               get_int(line, "Pad orientation:", pad_orientation);
+               get_int(line, "Radio setting:", radio_setting);
+               if (get_int(line, "Frequency:", radio_frequency))
+                       if (radio_frequency.get() < 0)
+                               radio_frequency.set(434550);
+               get_int(line, "Radio enable:", radio_enable);
+               get_int(line, "Storage size:", storage_size);
+               get_int(line, "Storage erase unit:", storage_erase_unit);
+               get_int(line, "flight", stored_flight);
+               get_string(line, "Callsign:", callsign);
+               get_string(line,"software-version", version);
+               get_string(line,"product", product);
+       }
+
+       final static int        serial_mode_read = 0;
+       final static int        serial_mode_save = 1;
+       final static int        serial_mode_reboot = 2;
+
+       class SerialData implements Runnable {
+               AltosConfig     config;
+               int             serial_mode;
+
+               void process_line(String line) {
+                       config.process_line(line);
+               }
+               void callback(String in_line) {
+                       final String line = in_line;
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               process_line(line);
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+
+               void reset_data() {
+                       serial.set(0);
+                       log_format.set(Altos.AO_LOG_FORMAT_UNKNOWN);
+                       main_deploy.set(250);
+                       apogee_delay.set(0);
+                       apogee_lockout.set(0);
+                       radio_channel.set(0);
+                       radio_setting.set(0);
+                       radio_frequency.set(0);
+                       radio_calibration.set(1186611);
+                       radio_enable.set(-1);
+                       flight_log_max.set(0);
+                       ignite_mode.set(-1);
+                       pad_orientation.set(-1);
+                       storage_size.set(-1);
+                       storage_erase_unit.set(-1);
+                       stored_flight.set(-1);
+                       callsign.set("N0CALL");
+                       version.set("unknown");
+                       product.set("unknown");
+               }
+
+               void get_data() {
+                       try {
+                               config.start_serial();
+                               reset_data();
+
+                               config.serial_line.printf("c s\nf\nl\nv\n");
+                               for (;;) {
+                                       try {
+                                               String line = config.serial_line.get_reply(5000);
+                                               if (line == null)
+                                                       stop_serial();
+                                               callback(line);
+                                               if (line.startsWith("software-version"))
+                                                       break;
+                                       } catch (Exception e) {
+                                               break;
+                                       }
+                               }
+                       } catch (InterruptedException ie) {
+                       } catch (TimeoutException te) {
+                       } finally {
+                               try {
+                                       stop_serial();
+                               } catch (InterruptedException ie) {
+                               }
+                       }
+                       callback("all finished");
+               }
+
+               void save_data() {
+                       try {
+                               double frequency = frequency();
+                               boolean has_frequency = radio_frequency.get() > 0;
+                               boolean has_setting = radio_setting.get() > 0;
+                               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 L %d\n", apogee_lockout.get());
+                               if (!remote)
+                                       serial_line.printf("c f %d\n", radio_calibration.get());
+                               serial_line.set_radio_frequency(frequency,
+                                                               has_frequency,
+                                                               has_setting,
+                                                               radio_calibration.get());
+                               if (remote) {
+                                       serial_line.stop_remote();
+                                       serial_line.set_radio_frequency(frequency);
+                                       AltosUIPreferences.set_frequency(device.getSerial(), frequency);
+                                       serial_line.start_remote();
+                               }
+                               serial_line.printf("c c %s\n", callsign.get());
+                               if (flight_log_max.get() != 0)
+                                       serial_line.printf("c l %d\n", flight_log_max.get());
+                               if (radio_enable.get() >= 0)
+                                       serial_line.printf("c e %d\n", radio_enable.get());
+                               if (ignite_mode.get() >= 0)
+                                       serial_line.printf("c i %d\n", ignite_mode.get());
+                               if (pad_orientation.get() >= 0)
+                                       serial_line.printf("c o %d\n", pad_orientation.get());
+                               serial_line.printf("c w\n");
+                       } catch (InterruptedException ie) {
+                       } catch (TimeoutException te) {
+                       } finally {
+                               try {
+                                       stop_serial();
+                               } catch (InterruptedException ie) {
+                               }
+                       }
+               }
+
+               void reboot() {
+                       try {
+                               start_serial();
+                               serial_line.printf("r eboot\n");
+                               serial_line.flush_output();
+                       } catch (InterruptedException ie) {
+                       } catch (TimeoutException te) {
+                       } finally {
+                               try {
+                                       stop_serial();
+                               } catch (InterruptedException ie) {
+                               }
+                               serial_line.close();
+                       }
+               }
+
+               public void run () {
+                       switch (serial_mode) {
+                       case serial_mode_save:
+                               save_data();
+                               /* fall through ... */
+                       case serial_mode_read:
+                               get_data();
+                               break;
+                       case serial_mode_reboot:
+                               reboot();
+                               break;
+                       }
+               }
+
+               public SerialData(AltosConfig in_config, int in_serial_mode) {
+                       config = in_config;
+                       serial_mode = in_serial_mode;
+               }
+       }
+
+       void run_serial_thread(int serial_mode) {
+               SerialData      sd = new SerialData(this, serial_mode);
+               Thread          st = new Thread(sd);
+               st.start();
+       }
+
+       void init_ui () throws InterruptedException, TimeoutException {
+               config_ui = new AltosConfigUI(owner, remote);
+               config_ui.addActionListener(this);
+               serial_line.set_frame(owner);
+               set_ui();
+       }
+
+       void abort() {
+               serial_line.close();
+               serial_line = null;
+               JOptionPane.showMessageDialog(owner,
+                                             String.format("Connection to \"%s\" failed",
+                                                           device.toShortString()),
+                                             "Connection Failed",
+                                             JOptionPane.ERROR_MESSAGE);
+               config_ui.setVisible(false);
+       }
+
+       void set_ui() throws InterruptedException, TimeoutException {
+               if (serial_line != null)
+                       run_serial_thread(serial_mode_read);
+               else
+                       update_ui();
+       }
+
+       double frequency() {
+               return AltosConvert.radio_to_frequency(radio_frequency.get(),
+                                                      radio_setting.get(),
+                                                      radio_calibration.get(),
+                                                      radio_channel.get());
+       }
+
+       void set_frequency(double freq) {
+               int     frequency = radio_frequency.get();
+               int     setting = radio_setting.get();
+
+               if (frequency > 0) {
+                       radio_frequency.set((int) Math.floor (freq * 1000 + 0.5));
+                       radio_channel.set(0);
+               } else if (setting > 0) {
+                       radio_setting.set(AltosConvert.radio_frequency_to_setting(freq,
+                                                                                 radio_calibration.get()));
+                       radio_channel.set(0);
+               } else {
+                       radio_channel.set(AltosConvert.radio_frequency_to_channel(freq));
+               }
+       }
+
+       void save_data() {
+
+               /* bounds check stuff */
+               if (config_ui.flight_log_max() > log_limit()) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Requested flight log, %dk, is larger than the available space, %dk.\n",
+                                                                   config_ui.flight_log_max(),
+                                                                   log_limit()),
+                                                     "Maximum Flight Log Too Large",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       return;
+               }
+
+               main_deploy.set(config_ui.main_deploy());
+               apogee_delay.set(config_ui.apogee_delay());
+               apogee_lockout.set(config_ui.apogee_lockout());
+               radio_calibration.set(config_ui.radio_calibration());
+               set_frequency(config_ui.radio_frequency());
+               flight_log_max.set(config_ui.flight_log_max());
+               if (radio_enable.get() >= 0)
+                       radio_enable.set(config_ui.radio_enable());
+               if (ignite_mode.get() >= 0)
+                       ignite_mode.set(config_ui.ignite_mode());
+               if (pad_orientation.get() >= 0)
+                       pad_orientation.set(config_ui.pad_orientation());
+               callsign.set(config_ui.callsign());
+               run_serial_thread(serial_mode_save);
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+               try {
+                       if (cmd.equals("Save")) {
+                               save_data();
+                       } else if (cmd.equals("Reset")) {
+                               set_ui();
+                       } else if (cmd.equals("Reboot")) {
+                               if (serial_line != null)
+                                       run_serial_thread(serial_mode_reboot);
+                       } else if (cmd.equals("Close")) {
+                               if (serial_line != null)
+                                       serial_line.close();
+                       }
+               } catch (InterruptedException ie) {
+                       abort();
+               } catch (TimeoutException te) {
+                       abort();
+               }
+       }
+
+       public AltosConfig(JFrame given_owner) {
+               owner = given_owner;
+
+               serial = new int_ref(0);
+               log_format = new int_ref(Altos.AO_LOG_FORMAT_UNKNOWN);
+               main_deploy = new int_ref(250);
+               apogee_delay = new int_ref(0);
+               apogee_lockout = new int_ref(0);
+               radio_channel = new int_ref(0);
+               radio_setting = new int_ref(0);
+               radio_frequency = new int_ref(0);
+               radio_calibration = new int_ref(1186611);
+               radio_enable = new int_ref(-1);
+               flight_log_max = new int_ref(0);
+               ignite_mode = new int_ref(-1);
+               pad_orientation = new int_ref(-1);
+               storage_size = new int_ref(-1);
+               storage_erase_unit = new int_ref(-1);
+               stored_flight = new int_ref(-1);
+               callsign = new string_ref("N0CALL");
+               version = new string_ref("unknown");
+               product = new string_ref("unknown");
+
+               device = AltosDeviceDialog.show(owner, Altos.product_any);
+               if (device != null) {
+                       try {
+                               serial_line = new AltosSerial(device);
+                               try {
+                                       if (!device.matchProduct(Altos.product_altimeter))
+                                               remote = true;
+                                       init_ui();
+                               } catch (InterruptedException ie) {
+                                       abort();
+                               } catch (TimeoutException te) {
+                                       abort();
+                               }
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(owner,
+                                                             ee.getMessage(),
+                                                             "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);
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosConfigFreqUI.java b/altosui/AltosConfigFreqUI.java
new file mode 100644 (file)
index 0000000..7958a21
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Copyright © 2011 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 javax.swing.plaf.basic.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.util.prefs.*;
+import java.util.concurrent.*;
+import org.altusmetrum.AltosLib.*;
+
+class AltosEditFreqUI extends AltosDialog implements ActionListener {
+       Frame           frame;
+       JTextField      frequency;
+       JTextField      description;
+       JButton         ok_button, cancel_button;
+       boolean         got_ok;
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if ("ok".equals(cmd)) {
+                       got_ok = true;
+                       setVisible(false);
+               }
+               if ("cancel".equals(cmd)) {
+                       got_ok = false;
+                       setVisible(false);
+               }
+       }
+
+       public AltosFrequency get() {
+               if (!got_ok)
+                       return null;
+
+               String  f_s = frequency.getText();
+               String  d_s = description.getText();
+
+               try {
+                       double  f_d = Double.parseDouble(f_s);
+
+                       return new AltosFrequency(f_d, d_s);
+               } catch (NumberFormatException ne) {
+               }
+               return null;
+       }
+
+       public AltosEditFreqUI(Frame in_frame, AltosFrequency existing) {
+               super(in_frame, true);
+
+               got_ok = false;
+               frame = in_frame;
+
+               Container pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               GridBagConstraints c = new GridBagConstraints();
+               c.insets = new Insets (4,4,4,4);
+               
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(new JLabel("Frequency"), c);
+
+               frequency = new JTextField(12);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 1;
+               c.gridy = 0;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(frequency, c);
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 0;
+               c.gridy = 1;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(new JLabel("Description"), c);
+
+               description = new JTextField(12);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 1;
+               c.gridy = 1;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(description, c);
+
+               ok_button = new JButton("OK");
+               ok_button.setActionCommand("ok");
+               ok_button.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 0;
+               c.gridy = 2;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(ok_button, c);
+               
+               cancel_button = new JButton("Cancel");
+               cancel_button.setActionCommand("cancel");
+               cancel_button.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 1;
+               c.gridy = 2;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(cancel_button, c);
+               
+               if (existing == null)
+                       setTitle("Add New Frequency");
+               else {
+                       setTitle("Edit Existing Frequency");
+                       frequency.setText(String.format("%7.3f", existing.frequency));
+                       description.setText(existing.description);
+               }
+               getRootPane().setDefaultButton(ok_button);
+
+               pack();
+               setLocationRelativeTo(frame);
+               
+       }
+
+       public AltosEditFreqUI(Frame in_frame) {
+               this(in_frame, (AltosFrequency) null);
+       }
+}
+
+public class AltosConfigFreqUI extends AltosDialog implements ActionListener {
+
+       Frame frame;
+       LinkedList<ActionListener> listeners;
+
+       class FrequencyList extends JList {
+               DefaultListModel list_model;
+
+               public void add(AltosFrequency frequency) {
+                       int i;
+                       for (i = 0; i < list_model.size(); i++) {
+                               AltosFrequency  f = (AltosFrequency) list_model.get(i);
+                               if (frequency.frequency == f.frequency)
+                                       return;
+                               if (frequency.frequency < f.frequency)
+                                       break;
+                       }
+                       list_model.insertElementAt(frequency, i);
+               }
+
+               public void remove(AltosFrequency frequency) {
+                       list_model.removeElement(frequency);
+               }
+
+               //Subclass JList to workaround bug 4832765, which can cause the
+               //scroll pane to not let the user easily scroll up to the beginning
+               //of the list.  An alternative would be to set the unitIncrement
+               //of the JScrollBar to a fixed value. You wouldn't get the nice
+               //aligned scrolling, but it should work.
+               public int getScrollableUnitIncrement(Rectangle visibleRect,
+                                                     int orientation,
+                                                     int direction) {
+                       int row;
+                       if (orientation == SwingConstants.VERTICAL &&
+                           direction < 0 && (row = getFirstVisibleIndex()) != -1) {
+                               Rectangle r = getCellBounds(row, row);
+                               if ((r.y == visibleRect.y) && (row != 0))  {
+                                       Point loc = r.getLocation();
+                                       loc.y--;
+                                       int prevIndex = locationToIndex(loc);
+                                       Rectangle prevR = getCellBounds(prevIndex, prevIndex);
+
+                                       if (prevR == null || prevR.y >= r.y) {
+                                               return 0;
+                                       }
+                                       return prevR.height;
+                               }
+                       }
+                       return super.getScrollableUnitIncrement(
+                               visibleRect, orientation, direction);
+               }
+
+               public AltosFrequency selected() {
+                       AltosFrequency  f = (AltosFrequency) getSelectedValue();
+                       return f;
+               }
+
+               public AltosFrequency[] frequencies() {
+                       AltosFrequency[]        ret;
+
+                       ret = new AltosFrequency[list_model.size()];
+                       for (int i = 0; i < list_model.size(); i++)
+                               ret[i] = (AltosFrequency) list_model.get(i);
+                       return ret;
+               }
+
+               public FrequencyList(AltosFrequency[] in_frequencies) {
+                       list_model = new DefaultListModel();
+                       setModel(list_model);
+                       setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+                       setLayoutOrientation(JList.HORIZONTAL_WRAP);
+                       for (int i = 0; i < in_frequencies.length; i++) {
+                               add(in_frequencies[i]);
+                       }
+                       setVisibleRowCount(in_frequencies.length);
+               }
+       }
+
+       FrequencyList   frequencies;
+
+       void save_frequencies() {
+               AltosUIPreferences.set_common_frequencies(frequencies.frequencies());
+       }
+
+       JButton add, edit, remove;
+
+       JButton cancel, ok;
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if ("ok".equals(cmd)) {
+                       save_frequencies();
+                       setVisible(false);
+               } else if ("cancel".equals(cmd)) {
+                       setVisible(false);
+               } else if ("add".equals(cmd)) {
+                       AltosEditFreqUI ui = new AltosEditFreqUI(frame);
+                       ui.setVisible(true);
+                       AltosFrequency  f = ui.get();
+                       if (f != null)
+                               frequencies.add(f);
+               } else if ("edit".equals(cmd)) {
+                       AltosFrequency  old_f = frequencies.selected();
+                       if (old_f == null)
+                               return;
+                       AltosEditFreqUI ui = new AltosEditFreqUI(frame, old_f);
+                       ui.setVisible(true);
+                       AltosFrequency  new_f = ui.get();
+                       if (new_f != null) {
+                               if (old_f != null)
+                                       frequencies.remove(old_f);
+                               frequencies.add(new_f);
+                       }
+               } else if ("remove".equals(cmd)) {
+                       AltosFrequency  old_f = frequencies.selected();
+                       if (old_f == null)
+                               return;
+                       int ret = JOptionPane.showConfirmDialog(this,
+                                                               String.format("Remove frequency \"%s\"?",
+                                                                             old_f.toShortString()),
+                                                               "Remove Frequency",
+                                                               JOptionPane.YES_NO_OPTION);
+                       if (ret == JOptionPane.YES_OPTION)
+                               frequencies.remove(old_f);
+               }
+       }
+
+       public AltosFrequency[] frequencies() {
+               return frequencies.frequencies();
+       }
+       
+       public AltosConfigFreqUI(Frame in_frame,
+                                AltosFrequency[] in_frequencies) {
+               super(in_frame, "Manage Frequencies", true);
+
+               frame = in_frame;
+
+               listeners = new LinkedList<ActionListener>();
+
+               frequencies = new FrequencyList(in_frequencies);
+
+               Container pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               GridBagConstraints c = new GridBagConstraints();
+               c.insets = new Insets(4,4,4,4);
+
+               /*
+                * Frequencies label and list
+                */
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 1;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(new JLabel("Frequencies"), c);
+
+               JScrollPane list_scroller = new JScrollPane(frequencies);
+               list_scroller.setAlignmentX(LEFT_ALIGNMENT);
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.WEST;
+               c.gridx = 0;
+               c.gridy = 1;
+               c.gridwidth = 6;
+               c.gridheight = 2;
+               c.weightx = 1;
+               c.weighty = 1;
+               pane.add(list_scroller, c);
+
+               add = new JButton("Add");
+               add.setActionCommand("add");
+               add.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0;
+               c.gridy = 3;
+               c.gridwidth = 2;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(add, c);
+
+               edit = new JButton("Edit");
+               edit.setActionCommand("edit");
+               edit.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 2;
+               c.gridy = 3;
+               c.gridwidth = 2;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(edit, c);
+
+               remove = new JButton("Remove");
+               remove.setActionCommand("remove");
+               remove.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 4;
+               c.gridy = 3;
+               c.gridwidth = 2;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(remove, c);
+
+               ok = new JButton("OK");
+               ok.setActionCommand("ok");
+               ok.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0;
+               c.gridy = 4;
+               c.gridwidth = 3;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(ok, c);
+
+               cancel = new JButton("Cancel");
+               cancel.setActionCommand("cancel");
+               cancel.addActionListener(this);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 3;
+               c.gridy = 4;
+               c.gridwidth = 3;
+               c.gridheight = 1;
+               c.weightx = 0;
+               c.weighty = 0;
+               pane.add(cancel, c);
+
+               pack();
+               setLocationRelativeTo(frame);
+       }
+
+       public static void show(Component frameComp) {
+               Frame   frame = JOptionPane.getFrameForComponent(frameComp);
+               AltosConfigFreqUI       dialog;
+
+               dialog = new AltosConfigFreqUI(frame, AltosUIPreferences.common_frequencies());
+               dialog.setVisible(true);
+       }
+
+}
diff --git a/altosui/AltosConfigTD.java b/altosui/AltosConfigTD.java
new file mode 100644 (file)
index 0000000..324a598
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * 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.*;
+
+import libaltosJNI.*;
+
+import org.altusmetrum.AltosLib.*;
+
+public class AltosConfigTD implements ActionListener {
+
+       class int_ref {
+               int     value;
+
+               public int get() {
+                       return value;
+               }
+               public void set(int i) {
+                       value = i;
+               }
+               public int_ref(int i) {
+                       value = i;
+               }
+       }
+
+       class string_ref {
+               String  value;
+
+               public String get() {
+                       return value;
+               }
+               public void set(String i) {
+                       value = i;
+               }
+               public string_ref(String i) {
+                       value = i;
+               }
+       }
+
+       JFrame          owner;
+       AltosDevice     device;
+       AltosSerial     serial_line;
+       int_ref         serial;
+       int_ref         radio_channel;
+       int_ref         radio_calibration;
+       int_ref         radio_setting;
+       int_ref         radio_frequency;
+       string_ref      config_version;
+       string_ref      version;
+       string_ref      product;
+       AltosConfigTDUI config_ui;
+       boolean         serial_started;
+       boolean         made_visible;
+
+       boolean get_int(String line, String label, int_ref x) {
+               if (line.startsWith(label)) {
+                       try {
+                               String tail = line.substring(label.length()).trim();
+                               String[] tokens = tail.split("\\s+");
+                               if (tokens.length > 0) {
+                                       int     i = Integer.parseInt(tokens[0]);
+                                       x.set(i);
+                                       return true;
+                               }
+                       } catch (NumberFormatException ne) {
+                       }
+               }
+               return false;
+       }
+
+       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;
+               }
+       }
+
+       void start_serial() throws InterruptedException, TimeoutException {
+               serial_started = true;
+       }
+
+       void stop_serial() throws InterruptedException {
+               if (!serial_started)
+                       return;
+               serial_started = false;
+       }
+
+       void update_ui() {
+               config_ui.set_serial(serial.get());
+               config_ui.set_product(product.get());
+               config_ui.set_version(version.get());
+               config_ui.set_radio_frequency(frequency());
+               config_ui.set_radio_calibration(radio_calibration.get());
+               config_ui.set_clean();
+               if (!made_visible) {
+                       made_visible = true;
+                       config_ui.make_visible();
+               }
+       }
+
+       void process_line(String line) {
+               if (line == null) {
+                       abort();
+                       return;
+               }
+               if (line.equals("all finished")) {
+                       if (serial_line != null)
+                               update_ui();
+                       return;
+               }
+               get_string(line, "Config version", config_version);
+               get_int(line, "serial-number", serial);
+               get_int(line, "Radio channel:", radio_channel);
+               get_int(line, "Radio cal:", radio_calibration);
+               get_int(line, "Frequency:", radio_frequency);
+               get_int(line, "Radio setting:", radio_setting);
+               get_string(line,"software-version", version);
+               get_string(line,"product", product);
+       }
+
+       final static int        serial_mode_read = 0;
+       final static int        serial_mode_save = 1;
+       final static int        serial_mode_reboot = 2;
+
+       class SerialData implements Runnable {
+               AltosConfigTD   config;
+               int             serial_mode;
+
+               void process_line(String line) {
+                       config.process_line(line);
+               }
+               void callback(String in_line) {
+                       final String line = in_line;
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               process_line(line);
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+
+               void reset_data() {
+                       serial.set(0);
+                       radio_channel.set(0);
+                       radio_setting.set(0);
+                       radio_frequency.set(0);
+                       radio_calibration.set(1186611);
+                       config_version.set("0.0");
+                       version.set("unknown");
+                       product.set("unknown");
+               }
+
+               void get_data() {
+                       try {
+                               boolean been_there = false;
+                               config.start_serial();
+                               reset_data();
+
+                               for (;;) {
+                                       config.serial_line.printf("c s\nf\nl\nv\n");
+                                       for (;;) {
+                                               try {
+                                                       String line = config.serial_line.get_reply(5000);
+                                                       if (line == null)
+                                                               stop_serial();
+                                                       callback(line);
+                                                       if (line.startsWith("software-version"))
+                                                               break;
+                                               } catch (Exception e) {
+                                                       break;
+                                               }
+                                       }
+                                       if (been_there)
+                                               break;
+                                       if (!config_version.get().equals("0.0"))
+                                               break;
+                                       been_there = true;
+                                       config.serial_line.printf("C\n ");
+                                       config.serial_line.flush_input();
+                               }
+                       } catch (InterruptedException ie) {
+                       } catch (TimeoutException te) {
+                       } finally {
+                               try {
+                                       stop_serial();
+                               } catch (InterruptedException ie) {
+                               }
+                       }
+                       double  pref_frequency = AltosPreferences.frequency(serial.get());
+                       if (pref_frequency != 0)
+                               radio_frequency.set((int) Math.floor (pref_frequency * 1000 + 0.5));
+                       callback("all finished");
+               }
+
+               void save_data() {
+                       double frequency = frequency();
+                       if (frequency != 0)
+                               AltosPreferences.set_frequency(serial.get(),
+                                                              frequency);
+               }
+
+               public void run () {
+                       switch (serial_mode) {
+                       case serial_mode_save:
+                               save_data();
+                               /* fall through ... */
+                       case serial_mode_read:
+                               get_data();
+                               break;
+                       }
+               }
+
+               public SerialData(AltosConfigTD in_config, int in_serial_mode) {
+                       config = in_config;
+                       serial_mode = in_serial_mode;
+               }
+       }
+
+       void run_serial_thread(int serial_mode) {
+               SerialData      sd = new SerialData(this, serial_mode);
+               Thread          st = new Thread(sd);
+               st.start();
+       }
+
+       void init_ui () throws InterruptedException, TimeoutException {
+               config_ui = new AltosConfigTDUI(owner);
+               config_ui.addActionListener(this);
+               serial_line.set_frame(owner);
+               set_ui();
+       }
+
+       void abort() {
+               serial_line.close();
+               serial_line = null;
+               JOptionPane.showMessageDialog(owner,
+                                             String.format("Connection to \"%s\" failed",
+                                                           device.toShortString()),
+                                             "Connection Failed",
+                                             JOptionPane.ERROR_MESSAGE);
+               config_ui.setVisible(false);
+       }
+
+       void set_ui() throws InterruptedException, TimeoutException {
+               if (serial_line != null)
+                       run_serial_thread(serial_mode_read);
+               else
+                       update_ui();
+       }
+
+       double frequency() {
+               return AltosConvert.radio_to_frequency(radio_frequency.get(),
+                                                      radio_setting.get(),
+                                                      radio_calibration.get(),
+                                                      radio_channel.get());
+       }
+
+       void set_frequency(double freq) {
+               int     frequency = radio_frequency.get();
+               int     setting = radio_setting.get();
+
+               if (frequency > 0) {
+                       radio_frequency.set((int) Math.floor (freq * 1000 + 0.5));
+               } else if (setting > 0) {
+                       radio_setting.set(AltosConvert.radio_frequency_to_setting(freq,
+                                                                                 radio_calibration.get()));
+                       radio_channel.set(0);
+               } else {
+                       radio_channel.set(AltosConvert.radio_frequency_to_channel(freq));
+               }
+       }
+
+       void save_data() {
+
+               set_frequency(config_ui.radio_frequency());
+               run_serial_thread(serial_mode_save);
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+               try {
+                       if (cmd.equals("Save")) {
+                               save_data();
+                       } else if (cmd.equals("Reset")) {
+                               set_ui();
+                       } else if (cmd.equals("Reboot")) {
+                               if (serial_line != null)
+                                       run_serial_thread(serial_mode_reboot);
+                       } else if (cmd.equals("Close")) {
+                               if (serial_line != null)
+                                       serial_line.close();
+                       }
+               } catch (InterruptedException ie) {
+                       abort();
+               } catch (TimeoutException te) {
+                       abort();
+               }
+       }
+
+       public AltosConfigTD(JFrame given_owner) {
+               owner = given_owner;
+
+               serial = new int_ref(0);
+               radio_channel = new int_ref(0);
+               radio_setting = new int_ref(0);
+               radio_frequency = new int_ref(0);
+               radio_calibration = new int_ref(1186611);
+               config_version = new string_ref("0.0");
+               version = new string_ref("unknown");
+               product = new string_ref("unknown");
+
+               device = AltosDeviceDialog.show(owner, Altos.product_basestation);
+               if (device != null) {
+                       try {
+                               serial_line = new AltosSerial(device);
+                               try {
+                                       init_ui();
+                               } catch (InterruptedException ie) {
+                                       abort();
+                               } catch (TimeoutException te) {
+                                       abort();
+                               }
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(owner,
+                                                             ee.getMessage(),
+                                                             "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);
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosConfigTDUI.java b/altosui/AltosConfigTDUI.java
new file mode 100644 (file)
index 0000000..f2058f6
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * 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;
+
+import libaltosJNI.*;
+
+import org.altusmetrum.AltosLib.*;
+
+public class AltosConfigTDUI
+       extends AltosDialog
+       implements ActionListener, ItemListener, DocumentListener
+{
+
+       Container       pane;
+       Box             box;
+       JLabel          product_label;
+       JLabel          version_label;
+       JLabel          serial_label;
+       JLabel          frequency_label;
+       JLabel          radio_calibration_label;
+       JLabel          radio_frequency_label;
+
+       public boolean          dirty;
+
+       JFrame          owner;
+       JLabel          product_value;
+       JLabel          version_value;
+       JLabel          serial_value;
+       AltosFreqList   radio_frequency_value;
+       JLabel          radio_calibration_value;
+
+       JButton         save;
+       JButton         reset;
+       JButton         reboot;
+       JButton         close;
+
+       ActionListener  listener;
+
+
+       /* A window listener to catch closing events and tell the config code */
+       class ConfigListener extends WindowAdapter {
+               AltosConfigTDUI ui;
+
+               public ConfigListener(AltosConfigTDUI this_ui) {
+                       ui = this_ui;
+               }
+
+               public void windowClosing(WindowEvent e) {
+                       ui.actionPerformed(new ActionEvent(e.getSource(),
+                                                          ActionEvent.ACTION_PERFORMED,
+                                                          "Close"));
+               }
+       }
+
+       /* Build the UI using a grid bag */
+       public AltosConfigTDUI(JFrame in_owner) {
+               super (in_owner, "Configure TeleDongle", false);
+
+               owner = in_owner;
+               GridBagConstraints c;
+
+               Insets il = new Insets(4,4,4,4);
+               Insets ir = new Insets(4,4,4,4);
+
+               pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               /* Product */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 0;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               product_label = new JLabel("Product:");
+               pane.add(product_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = 0;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               product_value = new JLabel("");
+               pane.add(product_value, c);
+
+               /* Version */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 1;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               version_label = new JLabel("Software version:");
+               pane.add(version_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = 1;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               version_value = new JLabel("");
+               pane.add(version_value, c);
+
+               /* Serial */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 2;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               serial_label = new JLabel("Serial:");
+               pane.add(serial_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = 2;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               serial_value = new JLabel("");
+               pane.add(serial_value, c);
+
+               /* Frequency */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 5;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               radio_frequency_label = new JLabel("Frequency:");
+               pane.add(radio_frequency_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = 5;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               radio_frequency_value = new AltosFreqList();
+               radio_frequency_value.addItemListener(this);
+               pane.add(radio_frequency_value, c);
+               radio_frequency_value.setToolTipText("Telemetry, RDF and packet frequency");
+
+               /* Radio Calibration */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 6;
+               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 JLabel(String.format("%d", 1186611));
+               pane.add(radio_calibration_value, c);
+
+               /* Buttons */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 12;
+               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");
+
+               c = new GridBagConstraints();
+               c.gridx = 2; c.gridy = 12;
+               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");
+
+               c = new GridBagConstraints();
+               c.gridx = 6; c.gridy = 12;
+               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");
+
+               addWindowListener(new ConfigListener(this));
+       }
+
+       /* Once the initial values are set, the config code will show the dialog */
+       public void make_visible() {
+               pack();
+               setLocationRelativeTo(owner);
+               setVisible(true);
+       }
+
+       /* If any values have been changed, confirm before closing */
+       public boolean check_dirty(String operation) {
+               if (dirty) {
+                       Object[] options = { String.format("%s anyway", operation), "Keep editing" };
+                       int i;
+                       i = JOptionPane.showOptionDialog(this,
+                                                        String.format("Configuration modified. %s anyway?", operation),
+                                                        "Configuration Modified",
+                                                        JOptionPane.DEFAULT_OPTION,
+                                                        JOptionPane.WARNING_MESSAGE,
+                                                        null, options, options[1]);
+                       if (i != 0)
+                               return false;
+               }
+               return true;
+       }
+
+       /* Listen for events from our buttons */
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("Close") || cmd.equals("Reboot"))
+                       if (!check_dirty(cmd))
+                               return;
+               listener.actionPerformed(e);
+               if (cmd.equals("Close") || cmd.equals("Reboot")) {
+                       setVisible(false);
+                       dispose();
+               }
+               dirty = false;
+       }
+
+       /* ItemListener interface method */
+       public void itemStateChanged(ItemEvent e) {
+               dirty = true;
+       }
+
+       /* DocumentListener interface methods */
+       public void changedUpdate(DocumentEvent e) {
+               dirty = true;
+       }
+
+       public void insertUpdate(DocumentEvent e) {
+               dirty = true;
+       }
+
+       public void removeUpdate(DocumentEvent e) {
+               dirty = true;
+       }
+
+       /* Let the config code hook on a listener */
+       public void addActionListener(ActionListener l) {
+               listener = l;
+       }
+
+       /* set and get all of the dialog values */
+       public void set_product(String product) {
+               radio_frequency_value.set_product(product);
+               product_value.setText(product);
+       }
+
+       public void set_version(String version) {
+               version_value.setText(version);
+       }
+
+       public void set_serial(int serial) {
+               radio_frequency_value.set_serial(serial);
+               serial_value.setText(String.format("%d", serial));
+       }
+
+       public void set_radio_frequency(double new_radio_frequency) {
+               int i;
+               for (i = 0; i < radio_frequency_value.getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) radio_frequency_value.getItemAt(i);
+                       
+                       if (f.close(new_radio_frequency)) {
+                               radio_frequency_value.setSelectedIndex(i);
+                               return;
+                       }
+               }
+               for (i = 0; i < radio_frequency_value.getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) radio_frequency_value.getItemAt(i);
+                       
+                       if (new_radio_frequency < f.frequency)
+                               break;
+               }
+               String  description = String.format("%s serial %s",
+                                                   product_value.getText(),
+                                                   serial_value.getText());
+               AltosFrequency  new_frequency = new AltosFrequency(new_radio_frequency, description);
+               AltosPreferences.add_common_frequency(new_frequency);
+               radio_frequency_value.insertItemAt(new_frequency, i);
+               radio_frequency_value.setSelectedIndex(i);
+       }
+
+       public double radio_frequency() {
+               return radio_frequency_value.frequency();
+       }
+
+       public void set_radio_calibration(int calibration) {
+               radio_calibration_value.setText(String.format("%d", calibration));
+       }
+
+       public void set_clean() {
+               dirty = false;
+       }
+}
diff --git a/altosui/AltosConfigUI.java b/altosui/AltosConfigUI.java
new file mode 100644 (file)
index 0000000..62394fa
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+ * 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;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosConfigUI
+       extends AltosDialog
+       implements ActionListener, ItemListener, DocumentListener
+{
+
+       Container       pane;
+       Box             box;
+       JLabel          product_label;
+       JLabel          version_label;
+       JLabel          serial_label;
+       JLabel          main_deploy_label;
+       JLabel          apogee_delay_label;
+       JLabel          apogee_lockout_label;
+       JLabel          frequency_label;
+       JLabel          radio_calibration_label;
+       JLabel          radio_frequency_label;
+       JLabel          radio_enable_label;
+       JLabel          flight_log_max_label;
+       JLabel          ignite_mode_label;
+       JLabel          pad_orientation_label;
+       JLabel          callsign_label;
+
+       public boolean          dirty;
+
+       JFrame          owner;
+       JLabel          product_value;
+       JLabel          version_value;
+       JLabel          serial_value;
+       JComboBox       main_deploy_value;
+       JComboBox       apogee_delay_value;
+       JComboBox       apogee_lockout_value;
+       AltosFreqList   radio_frequency_value;
+       JTextField      radio_calibration_value;
+       JRadioButton    radio_enable_value;
+       JComboBox       flight_log_max_value;
+       JComboBox       ignite_mode_value;
+       JComboBox       pad_orientation_value;
+       JTextField      callsign_value;
+
+       JButton         save;
+       JButton         reset;
+       JButton         reboot;
+       JButton         close;
+
+       ActionListener  listener;
+
+       static String[] main_deploy_values = {
+               "100", "150", "200", "250", "300", "350",
+               "400", "450", "500"
+       };
+
+       static String[] apogee_delay_values = {
+               "0", "1", "2", "3", "4", "5"
+       };
+
+       static String[] apogee_lockout_values = {
+               "0", "5", "10", "15", "20"
+       };
+
+       static String[] flight_log_max_values = {
+               "64", "128", "192", "256", "320",
+               "384", "448", "512", "576", "640",
+               "704", "768", "832", "896", "960",
+       };
+
+       static String[] ignite_mode_values = {
+               "Dual Deploy",
+               "Redundant Apogee",
+               "Redundant Main",
+       };
+
+       static String[] pad_orientation_values = {
+               "Antenna Up",
+               "Antenna Down",
+       };
+
+       /* A window listener to catch closing events and tell the config code */
+       class ConfigListener extends WindowAdapter {
+               AltosConfigUI   ui;
+
+               public ConfigListener(AltosConfigUI this_ui) {
+                       ui = this_ui;
+               }
+
+               public void windowClosing(WindowEvent e) {
+                       ui.actionPerformed(new ActionEvent(e.getSource(),
+                                                          ActionEvent.ACTION_PERFORMED,
+                                                          "Close"));
+               }
+       }
+
+       boolean is_telemini() {
+               String  product = product_value.getText();
+               return product != null && product.startsWith("TeleMini");
+       }
+
+       boolean is_telemetrum() {
+               String  product = product_value.getText();
+               return product != null && product.startsWith("TeleMetrum");
+       }
+
+       void set_radio_calibration_tool_tip() {
+               if (radio_calibration_value.isEnabled())
+                       radio_calibration_value.setToolTipText("Tune radio output to match desired frequency");
+               else
+                       radio_calibration_value.setToolTipText("Cannot tune radio while connected over packet mode");
+       }
+
+       void set_radio_enable_tool_tip() {
+               if (radio_enable_value.isEnabled())
+                       radio_enable_value.setToolTipText("Enable/Disable telemetry and RDF transmissions");
+               else
+                       radio_enable_value.setToolTipText("Firmware version does not support disabling radio");
+       }
+
+       void set_flight_log_max_tool_tip() {
+               if (flight_log_max_value.isEnabled())
+                       flight_log_max_value.setToolTipText("Size reserved for each flight log (in kB)");
+               else {
+                       if (is_telemetrum())
+                               flight_log_max_value.setToolTipText("Cannot set max value with flight logs in memory");
+                       else if (is_telemini())
+                               flight_log_max_value.setToolTipText("TeleMini stores only one flight");
+                       else
+                               flight_log_max_value.setToolTipText("Cannot set max flight log value");
+               }
+       }
+
+       void set_ignite_mode_tool_tip() {
+               if (ignite_mode_value.isEnabled())
+                       ignite_mode_value.setToolTipText("Select when igniters will be fired");
+               else
+                       ignite_mode_value.setToolTipText("Older firmware could not select ignite mode");
+       }
+
+       void set_pad_orientation_tool_tip() {
+               if (pad_orientation_value.isEnabled())
+                       pad_orientation_value.setToolTipText("How will TeleMetrum be mounted in the airframe");
+               else {
+                       if (is_telemetrum())
+                               pad_orientation_value.setToolTipText("Older TeleMetrum firmware must fly antenna forward");
+                       else if (is_telemini())
+                               pad_orientation_value.setToolTipText("TeleMini doesn't care how it is mounted");
+                       else
+                               pad_orientation_value.setToolTipText("Can't select orientation");
+               }
+       }
+
+       /* Build the UI using a grid bag */
+       public AltosConfigUI(JFrame in_owner, boolean remote) {
+               super (in_owner, "Configure TeleMetrum", false);
+
+               owner = in_owner;
+               GridBagConstraints c;
+               int row = 0;
+
+               Insets il = new Insets(4,4,4,4);
+               Insets ir = new Insets(4,4,4,4);
+
+               pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               /* Product */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               product_label = new JLabel("Product:");
+               pane.add(product_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               product_value = new JLabel("");
+               pane.add(product_value, c);
+               row++;
+
+               /* Version */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               version_label = new JLabel("Software version:");
+               pane.add(version_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               version_value = new JLabel("");
+               pane.add(version_value, c);
+               row++;
+
+               /* Serial */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               serial_label = new JLabel("Serial:");
+               pane.add(serial_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               serial_value = new JLabel("");
+               pane.add(serial_value, c);
+               row++;
+
+               /* Main deploy */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               main_deploy_label = new JLabel("Main Deploy Altitude(m):");
+               pane.add(main_deploy_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               main_deploy_value = new JComboBox(main_deploy_values);
+               main_deploy_value.setEditable(true);
+               main_deploy_value.addItemListener(this);
+               pane.add(main_deploy_value, c);
+               main_deploy_value.setToolTipText("Height above pad altitude to fire main charge");
+               row++;
+
+               /* Apogee delay */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               apogee_delay_label = new JLabel("Apogee Delay(s):");
+               pane.add(apogee_delay_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               apogee_delay_value = new JComboBox(apogee_delay_values);
+               apogee_delay_value.setEditable(true);
+               apogee_delay_value.addItemListener(this);
+               pane.add(apogee_delay_value, c);
+               apogee_delay_value.setToolTipText("Delay after apogee before charge fires");
+               row++;
+
+               /* Apogee lockout */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               apogee_lockout_label = new JLabel("Apogee Lockout(s):");
+               pane.add(apogee_lockout_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               apogee_lockout_value = new JComboBox(apogee_lockout_values);
+               apogee_lockout_value.setEditable(true);
+               apogee_lockout_value.addItemListener(this);
+               pane.add(apogee_lockout_value, c);
+               apogee_lockout_value.setToolTipText("Time after boost while apogee detection is locked out");
+               row++;
+
+               /* Frequency */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               radio_frequency_label = new JLabel("Frequency:");
+               pane.add(radio_frequency_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               radio_frequency_value = new AltosFreqList();
+               radio_frequency_value.addItemListener(this);
+               pane.add(radio_frequency_value, c);
+               radio_frequency_value.setToolTipText("Telemetry, RDF and packet frequency");
+               row++;
+
+               /* Radio Calibration */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               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 = row;
+               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);
+               set_radio_calibration_tool_tip();
+               row++;
+
+               /* Radio Enable */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               radio_enable_label = new JLabel("Telemetry/RDF Enable:");
+               pane.add(radio_enable_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               radio_enable_value = new JRadioButton("Enabled");
+               radio_enable_value.addItemListener(this);
+               pane.add(radio_enable_value, c);
+               set_radio_enable_tool_tip();
+               row++;
+
+               /* Callsign */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               callsign_label = new JLabel("Callsign:");
+               pane.add(callsign_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               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(AltosUIPreferences.callsign());
+               callsign_value.getDocument().addDocumentListener(this);
+               pane.add(callsign_value, c);
+               callsign_value.setToolTipText("Callsign reported in telemetry data");
+               row++;
+
+               /* Flight log max */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               flight_log_max_label = new JLabel("Maximum Flight Log Size:");
+               pane.add(flight_log_max_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               flight_log_max_value = new JComboBox(flight_log_max_values);
+               flight_log_max_value.setEditable(true);
+               flight_log_max_value.addItemListener(this);
+               pane.add(flight_log_max_value, c);
+               set_flight_log_max_tool_tip();
+               row++;
+
+               /* Ignite mode */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               ignite_mode_label = new JLabel("Igniter Firing Mode:");
+               pane.add(ignite_mode_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               ignite_mode_value = new JComboBox(ignite_mode_values);
+               ignite_mode_value.setEditable(false);
+               ignite_mode_value.addItemListener(this);
+               pane.add(ignite_mode_value, c);
+               set_ignite_mode_tool_tip();
+               row++;
+
+               /* Pad orientation */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               pad_orientation_label = new JLabel("Pad Orientation:");
+               pane.add(pad_orientation_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               c.gridwidth = 4;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               pad_orientation_value = new JComboBox(pad_orientation_values);
+               pad_orientation_value.setEditable(false);
+               pad_orientation_value.addItemListener(this);
+               pane.add(pad_orientation_value, c);
+               set_pad_orientation_tool_tip();
+               row++;
+
+               /* Buttons */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = row;
+               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");
+
+               c = new GridBagConstraints();
+               c.gridx = 2; c.gridy = row;
+               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");
+
+               c = new GridBagConstraints();
+               c.gridx = 4; c.gridy = row;
+               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 = row;
+               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");
+
+               addWindowListener(new ConfigListener(this));
+       }
+
+       /* Once the initial values are set, the config code will show the dialog */
+       public void make_visible() {
+               pack();
+               setLocationRelativeTo(owner);
+               setVisible(true);
+       }
+
+       /* If any values have been changed, confirm before closing */
+       public boolean check_dirty(String operation) {
+               if (dirty) {
+                       Object[] options = { String.format("%s anyway", operation), "Keep editing" };
+                       int i;
+                       i = JOptionPane.showOptionDialog(this,
+                                                        String.format("Configuration modified. %s anyway?", operation),
+                                                        "Configuration Modified",
+                                                        JOptionPane.DEFAULT_OPTION,
+                                                        JOptionPane.WARNING_MESSAGE,
+                                                        null, options, options[1]);
+                       if (i != 0)
+                               return false;
+               }
+               return true;
+       }
+
+       /* Listen for events from our buttons */
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("Close") || cmd.equals("Reboot"))
+                       if (!check_dirty(cmd))
+                               return;
+               listener.actionPerformed(e);
+               if (cmd.equals("Close") || cmd.equals("Reboot")) {
+                       setVisible(false);
+                       dispose();
+               }
+               dirty = false;
+       }
+
+       /* ItemListener interface method */
+       public void itemStateChanged(ItemEvent e) {
+               dirty = true;
+       }
+
+       /* DocumentListener interface methods */
+       public void changedUpdate(DocumentEvent e) {
+               dirty = true;
+       }
+
+       public void insertUpdate(DocumentEvent e) {
+               dirty = true;
+       }
+
+       public void removeUpdate(DocumentEvent e) {
+               dirty = true;
+       }
+
+       /* Let the config code hook on a listener */
+       public void addActionListener(ActionListener l) {
+               listener = l;
+       }
+
+       /* set and get all of the dialog values */
+       public void set_product(String product) {
+               radio_frequency_value.set_product(product);
+               product_value.setText(product);
+               set_pad_orientation_tool_tip();
+               set_flight_log_max_tool_tip();
+       }
+
+       public void set_version(String version) {
+               version_value.setText(version);
+       }
+
+       public void set_serial(int serial) {
+               radio_frequency_value.set_serial(serial);
+               serial_value.setText(String.format("%d", serial));
+       }
+
+       public void set_main_deploy(int new_main_deploy) {
+               main_deploy_value.setSelectedItem(Integer.toString(new_main_deploy));
+       }
+
+       public int main_deploy() {
+               return Integer.parseInt(main_deploy_value.getSelectedItem().toString());
+       }
+
+       public void set_apogee_delay(int new_apogee_delay) {
+               apogee_delay_value.setSelectedItem(Integer.toString(new_apogee_delay));
+       }
+
+       public int apogee_delay() {
+               return Integer.parseInt(apogee_delay_value.getSelectedItem().toString());
+       }
+
+       public void set_apogee_lockout(int new_apogee_lockout) {
+               apogee_lockout_value.setSelectedItem(Integer.toString(new_apogee_lockout));
+       }
+
+       public int apogee_lockout() {
+               return Integer.parseInt(apogee_lockout_value.getSelectedItem().toString());
+       }
+
+       public void set_radio_frequency(double new_radio_frequency) {
+               int i;
+               for (i = 0; i < radio_frequency_value.getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) radio_frequency_value.getItemAt(i);
+                       
+                       if (f.close(new_radio_frequency)) {
+                               radio_frequency_value.setSelectedIndex(i);
+                               return;
+                       }
+               }
+               for (i = 0; i < radio_frequency_value.getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) radio_frequency_value.getItemAt(i);
+                       
+                       if (new_radio_frequency < f.frequency)
+                               break;
+               }
+               String  description = String.format("%s serial %s",
+                                                   product_value.getText(),
+                                                   serial_value.getText());
+               AltosFrequency  new_frequency = new AltosFrequency(new_radio_frequency, description);
+               AltosUIPreferences.add_common_frequency(new_frequency);
+               radio_frequency_value.insertItemAt(new_frequency, i);
+               radio_frequency_value.setSelectedIndex(i);
+       }
+
+       public double radio_frequency() {
+               return radio_frequency_value.frequency();
+       }
+
+       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_radio_enable(int new_radio_enable) {
+               if (new_radio_enable >= 0) {
+                       radio_enable_value.setSelected(new_radio_enable > 0);
+                       radio_enable_value.setEnabled(true);
+               } else {
+                       radio_enable_value.setSelected(true);
+                       radio_enable_value.setEnabled(false);
+               }
+               set_radio_enable_tool_tip();
+       }
+
+       public int radio_enable() {
+               if (radio_enable_value.isEnabled())
+                       return radio_enable_value.isSelected() ? 1 : 0;
+               else
+                       return -1;
+       }
+
+       public void set_callsign(String new_callsign) {
+               callsign_value.setText(new_callsign);
+       }
+
+       public String callsign() {
+               return callsign_value.getText();
+       }
+
+       public void set_flight_log_max(int new_flight_log_max) {
+               if (new_flight_log_max == 0)
+                       flight_log_max_value.setEnabled(false);
+               flight_log_max_value.setSelectedItem(Integer.toString(new_flight_log_max));
+               set_flight_log_max_tool_tip();
+       }
+
+       public void set_flight_log_max_enabled(boolean enable) {
+               flight_log_max_value.setEnabled(enable);
+               set_flight_log_max_tool_tip();
+       }
+
+       public int flight_log_max() {
+               return Integer.parseInt(flight_log_max_value.getSelectedItem().toString());
+       }
+
+       public void set_flight_log_max_limit(int flight_log_max_limit) {
+               boolean any_added = false;
+               flight_log_max_value.removeAllItems();
+               for (int i = 0; i < flight_log_max_values.length; i++) {
+                       if (Integer.parseInt(flight_log_max_values[i]) < flight_log_max_limit){
+                               flight_log_max_value.addItem(flight_log_max_values[i]);
+                               any_added = true;
+                       }
+               }
+               flight_log_max_value.addItem(String.format("%d", flight_log_max_limit));
+       }
+
+       public void set_ignite_mode(int new_ignite_mode) {
+               if (new_ignite_mode >= ignite_mode_values.length)
+                       new_ignite_mode = 0;
+               if (new_ignite_mode < 0) {
+                       ignite_mode_value.setEnabled(false);
+                       new_ignite_mode = 0;
+               } else {
+                       ignite_mode_value.setEnabled(true);
+               }
+               ignite_mode_value.setSelectedIndex(new_ignite_mode);
+               set_ignite_mode_tool_tip();
+       }
+
+       public int ignite_mode() {
+               if (ignite_mode_value.isEnabled())
+                       return ignite_mode_value.getSelectedIndex();
+               else
+                       return -1;
+       }
+
+
+       public void set_pad_orientation(int new_pad_orientation) {
+               if (new_pad_orientation >= pad_orientation_values.length)
+                       new_pad_orientation = 0;
+               if (new_pad_orientation < 0) {
+                       pad_orientation_value.setEnabled(false);
+                       new_pad_orientation = 0;
+               } else {
+                       pad_orientation_value.setEnabled(true);
+               }
+               pad_orientation_value.setSelectedIndex(new_pad_orientation);
+               set_pad_orientation_tool_tip();
+       }
+
+       public int pad_orientation() {
+               if (pad_orientation_value.isEnabled())
+                       return pad_orientation_value.getSelectedIndex();
+               else
+                       return -1;
+       }
+
+       public void set_clean() {
+               dirty = false;
+       }
+}
diff --git a/altosui/AltosConfigureUI.java b/altosui/AltosConfigureUI.java
new file mode 100644 (file)
index 0000000..da82e8e
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * 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 java.beans.*;
+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;
+import javax.swing.plaf.basic.*;
+import org.altusmetrum.AltosLib.*;
+
+class DelegatingRenderer implements ListCellRenderer {
+
+       // ...
+       public static void install(JComboBox comboBox) {
+               DelegatingRenderer renderer = new DelegatingRenderer(comboBox);
+               renderer.initialise();
+               comboBox.setRenderer(renderer);
+       }
+
+       // ...
+       private final JComboBox comboBox;
+
+       // ...
+       private ListCellRenderer delegate;
+
+       // ...
+       private DelegatingRenderer(JComboBox comboBox) {
+               this.comboBox = comboBox;
+       }
+
+       // ...
+       private void initialise() {
+               delegate = new JComboBox().getRenderer();
+               comboBox.addPropertyChangeListener("UI", new PropertyChangeListener() {
+
+                               public void propertyChange(PropertyChangeEvent evt) {
+                                       delegate = new JComboBox().getRenderer();
+                               }
+                       });
+       }
+
+       // ...
+       public Component getListCellRendererComponent(JList list,
+                                                     Object value, int index, boolean isSelected, boolean cellHasFocus) {
+
+               return delegate.getListCellRendererComponent(list,
+                                                            ((UIManager.LookAndFeelInfo) value).getName(),
+                                                            index, isSelected, cellHasFocus);
+       }
+}
+
+public class AltosConfigureUI
+       extends AltosDialog
+       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;
+
+       JRadioButton    imperial_units;
+
+       JLabel          font_size_label;
+       JComboBox       font_size_value;
+
+       JLabel          look_and_feel_label;
+       JComboBox       look_and_feel_value;
+
+       JRadioButton    serial_debug;
+
+       JButton         manage_bluetooth;
+       JButton         manage_frequencies;
+
+       final static String[] font_size_names = { "Small", "Medium", "Large" };
+
+       /* DocumentListener interface methods */
+       public void changedUpdate(DocumentEvent e) {
+               AltosUIPreferences.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);
+
+               int row = 0;
+
+               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 = row++;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               pane.add(new JLabel ("Configure AltOS UI"), c);
+
+               c.gridx = 0;
+               c.gridy = row++;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               pane.add(new JLabel (String.format("AltOS version %s", AltosVersion.version)), c);
+
+               /* Voice settings */
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(new JLabel("Voice"), c);
+
+               enable_voice = new JRadioButton("Enable", AltosUIPreferences.voice());
+               enable_voice.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       JRadioButton item = (JRadioButton) e.getSource();
+                                       boolean enabled = item.isSelected();
+                                       AltosUIPreferences.set_voice(enabled);
+                                       if (enabled)
+                                               voice.speak_always("Enable voice.");
+                                       else
+                                               voice.speak_always("Disable voice.");
+                               }
+                       });
+               c.gridx = 1;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.weightx = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(enable_voice, c);
+               enable_voice.setToolTipText("Enable/Disable all audio in-flight announcements");
+
+               c.gridx = 2;
+               c.gridy = row++;
+               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);
+               test_voice.setToolTipText("Play a stock audio clip to check volume");
+
+               /* Log directory settings */
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(new JLabel("Log Directory"), c);
+
+               configure_log = new JButton(AltosUIPreferences.logdir().getPath());
+               configure_log.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       AltosUIPreferences.ConfigureLog();
+                                       configure_log.setText(AltosUIPreferences.logdir().getPath());
+                               }
+                       });
+               c.gridx = 1;
+               c.gridy = row++;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(configure_log, c);
+               configure_log.setToolTipText("Which directory flight logs are stored in");
+
+               /* Callsign setting */
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(new JLabel("Callsign"), c);
+
+               callsign_value = new JTextField(AltosUIPreferences.callsign());
+               callsign_value.getDocument().addDocumentListener(this);
+               c.gridx = 1;
+               c.gridy = row++;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(callsign_value, c);
+               callsign_value.setToolTipText("Callsign sent in packet mode");
+
+               /* Imperial units setting */
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(new JLabel("Imperial Units"), c);
+
+               imperial_units = new JRadioButton("Enable", AltosUIPreferences.serial_debug());
+               imperial_units.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       JRadioButton item = (JRadioButton) e.getSource();
+                                       boolean enabled = item.isSelected();
+                                       AltosUIPreferences.set_imperial_units(enabled);
+                               }
+                       });
+               imperial_units.setToolTipText("Use Imperial units instead of metric");
+
+               c.gridx = 1;
+               c.gridy = row++;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(imperial_units, c);
+
+               /* Font size setting */
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(new JLabel("Font size"), c);
+
+               font_size_value = new JComboBox(font_size_names);
+               int font_size = AltosUIPreferences.font_size();
+               font_size_value.setSelectedIndex(font_size - Altos.font_size_small);
+               font_size_value.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       int     size = font_size_value.getSelectedIndex() + Altos.font_size_small;
+
+                                       AltosUIPreferences.set_font_size(size);
+                               }
+                       });
+               c.gridx = 1;
+               c.gridy = row++;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(font_size_value, c);
+               font_size_value.setToolTipText("Font size used in telemetry window");
+
+               /* Look & Feel setting */
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(new JLabel("Look & feel"), c);
+
+               class LookAndFeelRenderer extends BasicComboBoxRenderer implements ListCellRenderer {
+
+                       public LookAndFeelRenderer() {
+                               super();
+                       }
+
+                       public Component getListCellRendererComponent(
+                               JList list,
+                               Object value,
+                               int index,
+                               boolean isSelected,
+                               boolean cellHasFocus)
+                       {
+                               super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+                               setText(((UIManager.LookAndFeelInfo) value).getName());
+                               return this;
+                       }
+               }
+
+               final UIManager.LookAndFeelInfo[] look_and_feels = UIManager.getInstalledLookAndFeels();
+
+               look_and_feel_value = new JComboBox(look_and_feels);
+
+               DelegatingRenderer.install(look_and_feel_value);
+
+               String look_and_feel  = AltosUIPreferences.look_and_feel();
+               for (int i = 0; i < look_and_feels.length; i++)
+                       if (look_and_feel.equals(look_and_feels[i].getClassName()))
+                               look_and_feel_value.setSelectedIndex(i);
+
+               look_and_feel_value.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       int     id = look_and_feel_value.getSelectedIndex();
+
+                                       AltosUIPreferences.set_look_and_feel(look_and_feels[id].getClassName());
+                               }
+                       });
+               c.gridx = 1;
+               c.gridy = row++;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(look_and_feel_value, c);
+               look_and_feel_value.setToolTipText("Look&feel used for new windows");
+
+               /* Serial debug setting */
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 1;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(new JLabel("Serial Debug"), c);
+
+               serial_debug = new JRadioButton("Enable", AltosUIPreferences.serial_debug());
+               serial_debug.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       JRadioButton item = (JRadioButton) e.getSource();
+                                       boolean enabled = item.isSelected();
+                                       AltosUIPreferences.set_serial_debug(enabled);
+                               }
+                       });
+               serial_debug.setToolTipText("Enable/Disable USB I/O getting sent to the console");
+
+               c.gridx = 1;
+               c.gridy = row++;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(serial_debug, c);
+
+               manage_bluetooth = new JButton("Manage Bluetooth");
+               manage_bluetooth.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       AltosBTManage.show(owner, AltosBTKnown.bt_known());
+                               }
+                       });
+               c.gridx = 0;
+               c.gridy = row;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(manage_bluetooth, c);
+
+               manage_frequencies = new JButton("Manage Frequencies");
+               manage_frequencies.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       AltosConfigFreqUI.show(owner);
+                               }
+                       });
+               manage_frequencies.setToolTipText("Configure which values are shown in frequency menus");
+               c.gridx = 2;
+               c.gridx = 2;
+               c.gridy = row++;
+               c.gridwidth = 2;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.WEST;
+               pane.add(manage_frequencies, 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 = row++;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               pane.add(close, c);
+
+               pack();
+               setLocationRelativeTo(owner);
+               setVisible(true);
+       }
+}
diff --git a/altosui/AltosDataChooser.java b/altosui/AltosDataChooser.java
new file mode 100644 (file)
index 0000000..4bd51c3
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosDataChooser extends JFileChooser {
+       JFrame  frame;
+       String  filename;
+       File    file;
+
+       public String filename() {
+               return filename;
+       }
+
+       public File file() {
+               return file;
+       }
+
+       public AltosRecordIterable 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 AltosEepromIterable(in);
+                               } else if (filename.endsWith("telem")) {
+                                       FileInputStream in = new FileInputStream(file);
+                                       return new AltosTelemetryIterable(in);
+                               } else if (filename.endsWith("mega")) {
+                                       FileInputStream in = new FileInputStream(file);
+                                       return new AltosEepromMegaIterable(in);
+                               } else {
+                                       throw new FileNotFoundException();
+                               }
+                       } catch (FileNotFoundException fe) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             fe.getMessage(),
+                                                             "Cannot open file",
+                                                             JOptionPane.ERROR_MESSAGE);
+                       }
+               }
+               return null;
+       }
+
+       public AltosDataChooser(JFrame in_frame) {
+               frame = in_frame;
+               setDialogTitle("Select Flight Record File");
+               setFileFilter(new FileNameExtensionFilter("Flight data file",
+                                                         "telem", "eeprom", "mega"));
+               setCurrentDirectory(AltosUIPreferences.logdir());
+       }
+}
diff --git a/altosui/AltosDataPoint.java b/altosui/AltosDataPoint.java
new file mode 100644 (file)
index 0000000..5e07732
--- /dev/null
@@ -0,0 +1,30 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+interface AltosDataPoint {
+    int version();
+    int serial();
+    int flight();
+    String callsign();
+    double time();
+    double rssi();
+
+    int state();
+    String state_name();
+
+    double acceleration();
+    double pressure();
+    double altitude();
+    double height();
+    double accel_speed();
+    double baro_speed();
+    double temperature();
+    double battery_voltage();
+    double drogue_voltage();
+    double main_voltage();
+    boolean has_accel();
+}
+
diff --git a/altosui/AltosDataPointReader.java b/altosui/AltosDataPointReader.java
new file mode 100644 (file)
index 0000000..821b077
--- /dev/null
@@ -0,0 +1,86 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.lang.UnsupportedOperationException;
+import java.util.NoSuchElementException;
+import java.util.Iterator;
+import org.altusmetrum.AltosLib.*;
+
+class AltosDataPointReader implements Iterable<AltosDataPoint> {
+    Iterator<AltosRecord> iter;
+    AltosState state;
+    AltosRecord record;
+    boolean has_gps;
+    boolean has_accel;
+    boolean has_ignite;
+
+    final static int MISSING = AltosRecord.MISSING;
+
+    public AltosDataPointReader(AltosRecordIterable reader) {
+        this.iter = reader.iterator();
+        this.state = null;
+       has_accel = reader.has_accel();
+       has_gps = reader.has_gps();
+       has_ignite = reader.has_ignite();
+    }
+
+    private void read_next_record() 
+        throws NoSuchElementException
+    {
+        record = iter.next();
+        state = new AltosState(record, state);
+    }
+
+    private AltosDataPoint current_dp() {
+        assert this.record != null;
+        
+        return new AltosDataPoint() {
+            public int version() { return record.version; }
+            public int serial() { return record.serial; }
+            public int flight() { return record.flight; }
+            public String callsign() { return record.callsign; }
+            public double time() { return record.time; }
+            public double rssi() { return record.rssi; }
+
+            public int state() { return record.state; }
+            public String state_name() { return record.state(); }
+
+            public double acceleration() { return record.acceleration(); }
+            public double pressure() { return record.raw_pressure(); }
+            public double altitude() { return record.raw_altitude(); }
+           public double height() { return record.raw_height(); }
+            public double accel_speed() { return record.accel_speed(); }
+            public double baro_speed() { return state.baro_speed; }
+            public double temperature() { return record.temperature(); }
+            public double battery_voltage() { return record.battery_voltage(); }
+            public double drogue_voltage() { return record.drogue_voltage(); }
+            public double main_voltage() { return record.main_voltage(); }
+           public boolean has_accel() { return has_accel; }
+        };
+    }
+
+    public Iterator<AltosDataPoint> iterator() {
+        return new Iterator<AltosDataPoint>() {
+            public void remove() { 
+                throw new UnsupportedOperationException(); 
+            }
+            public boolean hasNext() {
+               if (record != null && record.state == Altos.ao_flight_landed)
+                   return false;
+                return iter.hasNext();
+            }
+            public AltosDataPoint next() {
+               do {
+                   read_next_record();
+               } while (record.time < -1.0 && hasNext());
+                return current_dp();
+            }
+        };
+    }
+}
+
diff --git a/altosui/AltosDebug.java b/altosui/AltosDebug.java
new file mode 100644 (file)
index 0000000..23e38bc
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * 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.io.*;
+import java.util.concurrent.*;
+import java.util.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosDebug extends AltosSerial {
+
+       public static final byte WR_CONFIG =            0x1d;
+       public static final byte RD_CONFIG =            0x24;
+       public static final byte CONFIG_TIMERS_OFF =            (1 << 3);
+       public static final byte CONFIG_DMA_PAUSE =             (1 << 2);
+       public static final byte CONFIG_TIMER_SUSPEND =         (1 << 1);
+       public static final byte SET_FLASH_INFO_PAGE =          (1 << 0);
+
+       public static final byte GET_PC =               0x28;
+       public static final byte READ_STATUS =          0x34;
+       public static final byte STATUS_CHIP_ERASE_DONE =       (byte) (1 << 7);
+       public static final byte STATUS_PCON_IDLE =             (1 << 6);
+       public static final byte STATUS_CPU_HALTED =            (1 << 5);
+       public static final byte STATUS_POWER_MODE_0 =          (1 << 4);
+       public static final byte STATUS_HALT_STATUS =           (1 << 3);
+       public static final byte STATUS_DEBUG_LOCKED =          (1 << 2);
+       public static final byte STATUS_OSCILLATOR_STABLE =     (1 << 1);
+       public static final byte STATUS_STACK_OVERFLOW =        (1 << 0);
+
+       public static final byte SET_HW_BRKPNT =        0x3b;
+       public static byte       HW_BRKPNT_N(byte n)    { return (byte) ((n) << 3); }
+       public static final byte HW_BRKPNT_N_MASK =             (0x3 << 3);
+       public static final byte HW_BRKPNT_ENABLE =             (1 << 2);
+
+       public static final byte HALT =                 0x44;
+       public static final byte RESUME =               0x4c;
+       public static       byte DEBUG_INSTR(byte n)    { return (byte) (0x54|(n)); }
+       public static final byte STEP_INSTR =           0x5c;
+       public static        byte STEP_REPLACE(byte n)  { return  (byte) (0x64|(n)); }
+       public static final byte GET_CHIP_ID =          0x68;
+
+
+       boolean debug_mode;
+
+       void ensure_debug_mode() {
+               if (!debug_mode) {
+                       printf("D\n");
+                       try {
+                               flush_input();
+                       } catch (InterruptedException ie) {
+                       }
+                       debug_mode = true;
+               }
+       }
+
+       void dump_memory(String header, int address, byte[] bytes, int start, int len) {
+               System.out.printf("%s\n", header);
+               for (int j = 0; j < len; j++) {
+                       if ((j & 15) == 0) {
+                               if (j != 0)
+                                       System.out.printf("\n");
+                               System.out.printf ("%04x:", address + j);
+                       }
+                       System.out.printf(" %02x", bytes[start + j]);
+               }
+               System.out.printf("\n");
+       }
+
+       /*
+        * Write target memory
+        */
+       public void write_memory(int address, byte[] bytes, int start, int len) {
+               ensure_debug_mode();
+//             dump_memory("write_memory", address, bytes, start, len);
+               printf("O %x %x\n", len, address);
+               for (int i = 0; i < len; i++)
+                       printf("%02x", bytes[start + i]);
+       }
+
+       public void write_memory(int address, byte[] bytes) {
+               write_memory(address, bytes, 0, bytes.length);
+       }
+
+       /*
+        * Read target memory
+        */
+       public byte[] read_memory(int address, int length)
+               throws IOException, InterruptedException {
+               byte[]  data = new byte[length];
+
+               flush_input();
+               ensure_debug_mode();
+               printf("I %x %x\n", length, address);
+               int i = 0;
+               int start = 0;
+               while (i < length) {
+                       String  line = get_reply().trim();
+                       if (!Altos.ishex(line) || line.length() % 2 != 0)
+                               throw new IOException(
+                                       String.format
+                                       ("Invalid reply \"%s\"", line));
+                       int this_time = line.length() / 2;
+                       for (int j = 0; j < this_time; j++)
+                               data[start + j] = (byte) ((Altos.fromhex(line.charAt(j*2)) << 4) +
+                                                 Altos.fromhex(line.charAt(j*2+1)));
+                       start += this_time;
+                       i += this_time;
+               }
+//             dump_memory("read_memory", address, data, 0, length);
+
+               return data;
+       }
+
+       /*
+        * Write raw bytes to the debug link using the 'P' command
+        */
+       public void write_bytes(byte[] bytes) throws IOException {
+               int i = 0;
+               ensure_debug_mode();
+               while (i < bytes.length) {
+                       int this_time = bytes.length - i;
+                       if (this_time > 8)
+                               this_time = 0;
+                       printf("P");
+                       for (int j = 0; j < this_time; j++)
+                               printf(" %02x", bytes[i+j]);
+                       printf("\n");
+                       i += this_time;
+               }
+       }
+
+       public void write_byte(byte b) throws IOException {
+               byte[] bytes = { b };
+               write_bytes(bytes);
+       }
+
+       /*
+        * Read raw bytes from the debug link using the 'G' command
+        */
+       public byte[] read_bytes(int length)
+               throws IOException, InterruptedException {
+
+               flush_input();
+               ensure_debug_mode();
+               printf("G %x\n", length);
+               int i = 0;
+               byte[] data = new byte[length];
+               while (i < length) {
+                       String line = get_reply();
+
+                       if (line == null)
+                               throw new IOException("Timeout in read_bytes");
+                       line = line.trim();
+                       String tokens[] = line.split("\\s+");
+                       for (int j = 0; j < tokens.length; j++) {
+                               if (!Altos.ishex(tokens[j]) ||
+                                   tokens[j].length() != 2)
+                                       throw new IOException(
+                                               String.format
+                                               ("Invalid read_bytes reply \"%s\"", line));
+                               try {
+                                       if (i + j >= length)
+                                               throw new IOException(
+                                                       String.format
+                                                       ("Invalid read_bytes reply \"%s\"", line));
+                                       else
+                                               data[i + j] = (byte) Integer.parseInt(tokens[j], 16);
+                               } catch (NumberFormatException ne) {
+                                       throw new IOException(
+                                               String.format
+                                               ("Invalid read_bytes reply \"%s\"", line));
+                               }
+                       }
+                       i += tokens.length;
+               }
+               return data;
+       }
+
+       public byte read_byte() throws IOException, InterruptedException {
+               return read_bytes(1)[0];
+       }
+
+       public byte debug_instr(byte[] instruction) throws IOException, InterruptedException {
+               byte[] command = new byte[1 + instruction.length];
+               command[0] = DEBUG_INSTR((byte) instruction.length);
+               for (int i = 0; i < instruction.length; i++)
+                       command[i+1] = instruction[i];
+               write_bytes(command);
+               return read_byte();
+       }
+
+       public byte resume() throws IOException, InterruptedException {
+               write_byte(RESUME);
+               return read_byte();
+       }
+
+       public int read_uint16() throws IOException, InterruptedException {
+               byte[] d = read_bytes(2);
+               return ((int) (d[0] & 0xff) << 8) | (d[1] & 0xff);
+       }
+
+       public int read_uint8()  throws IOException, InterruptedException {
+               byte[] d = read_bytes(1);
+               return (int) (d[0] & 0xff);
+       }
+
+       public int get_chip_id() throws IOException, InterruptedException {
+               write_byte(GET_CHIP_ID);
+               return read_uint16();
+       }
+
+       public int get_pc() throws IOException, InterruptedException {
+               write_byte(GET_PC);
+               return read_uint16();
+       }
+
+       public byte read_status() throws IOException, InterruptedException {
+               write_byte(READ_STATUS);
+               return read_byte();
+       }
+
+       static final byte LJMP                  = 0x02;
+
+       public void set_pc(int pc) throws IOException, InterruptedException {
+               byte high = (byte) (pc >> 8);
+               byte low = (byte) pc;
+               byte[] jump_mem = { LJMP, high, low };
+               debug_instr(jump_mem);
+       }
+
+       public boolean check_connection() throws IOException, InterruptedException {
+               byte reply = read_status();
+               if ((reply & STATUS_CHIP_ERASE_DONE) == 0)
+                       return false;
+               if ((reply & STATUS_PCON_IDLE) != 0)
+                       return false;
+               if ((reply & STATUS_POWER_MODE_0) == 0)
+                       return false;
+               return true;
+       }
+
+       public AltosRomconfig romconfig() {
+               try {
+                       byte[] bytes = read_memory(0xa0, 10);
+                       return new AltosRomconfig(bytes, 0);
+               } catch (IOException ie) {
+               } catch (InterruptedException ie) {
+               }
+               return new AltosRomconfig();
+       }
+
+       /*
+        * Reset target
+        */
+       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 (file)
index 0000000..6225881
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+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 show() {
+                       label.setVisible(true);
+                       value.setVisible(true);
+                       lights.setVisible(true);
+               }
+
+               void hide() {
+                       label.setVisible(false);
+                       value.setVisible(false);
+                       lights.setVisible(false);
+               }
+
+               void reset() {
+                       value.setText("");
+                       lights.set(false);
+               }
+
+               void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+               }
+
+               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() {
+                       label.setVisible(true);
+                       value.setVisible(true);
+               }
+
+               void hide() {
+                       label.setVisible(false);
+                       value.setVisible(false);
+               }
+
+               void show(AltosUnits units, double v) {
+                       value.setText(units.show(8, v));
+               }
+
+               void show(String format, double v) {
+                       value.setText(String.format(format, v));
+               }
+
+               void show(String v) {
+                       value.setText(v);
+               }
+
+               void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+               }
+
+               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("");
+               }
+
+               void show() {
+                       label.setVisible(true);
+                       value1.setVisible(true);
+                       value2.setVisible(true);
+               }
+
+               void hide() {
+                       label.setVisible(false);
+                       value1.setVisible(false);
+                       value2.setVisible(false);
+               }
+
+               void set_font() {
+                       label.setFont(Altos.label_font);
+                       value1.setFont(Altos.value_font);
+                       value2.setFont(Altos.value_font);
+               }
+
+               abstract void show(AltosState state, int crc_errors);
+
+               void show(String v1, String v2) {
+                       show();
+                       value1.setText(v1);
+                       value2.setText(v2);
+               }
+               void show(String f1, double v1, String f2, double v2) {
+                       show();
+                       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(AltosConvert.height, 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(AltosConvert.speed, 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 && state.gps.connected)
+                               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 && state.gps.connected)
+                               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) {
+                       show();
+                       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) {
+                       show();
+                       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(AltosConvert.distance, 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 set_font() {
+               lat.set_font();
+               lon.set_font();
+               height.set_font();
+               speed.set_font();
+               bearing.set_font();
+               range.set_font();
+               elevation.set_font();
+               main.set_font();
+               apogee.set_font();
+       }
+
+       public void show(AltosState state, int crc_errors) {
+               height.show(state, crc_errors);
+               speed.show(state, crc_errors);
+               if (state.gps != null && state.gps.connected) {
+                       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);
+               } else {
+                       bearing.hide();
+                       range.hide();
+                       elevation.hide();
+                       lat.hide();
+                       lon.hide();
+               }
+               if (state.main_sense != AltosRecord.MISSING)
+                       main.show(state, crc_errors);
+               else
+                       main.hide();
+               if (state.drogue_sense != AltosRecord.MISSING)
+                       apogee.show(state, crc_errors);
+               else
+                       apogee.hide();
+       }
+
+       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/altosui/AltosDevice.java b/altosui/AltosDevice.java
new file mode 100644 (file)
index 0000000..1b5c1a9
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright © 2011 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.util.*;
+import libaltosJNI.*;
+
+public interface AltosDevice {
+       public abstract String toString();
+       public abstract String toShortString();
+       public abstract int getSerial();
+       public abstract String getPath();
+       public abstract boolean matchProduct(int product);
+       public abstract String getErrorString();
+       public SWIGTYPE_p_altos_file open();
+}
diff --git a/altosui/AltosDeviceDialog.java b/altosui/AltosDeviceDialog.java
new file mode 100644 (file)
index 0000000..fa9d001
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * 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.util.*;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.*;
+import libaltosJNI.*;
+
+public class AltosDeviceDialog extends AltosDialog implements ActionListener {
+
+       private AltosDevice     value;
+       private JList           list;
+       private JButton         cancel_button;
+       private JButton         select_button;
+       private JButton         manage_bluetooth_button;
+       private Frame           frame;
+       private int             product;
+
+       private AltosDevice getValue() {
+               return value;
+       }
+
+       private AltosDevice[] devices() {
+               java.util.List<AltosDevice>     usb_devices = AltosUSBDevice.list(product);
+               int                             num_devices = usb_devices.size();
+               java.util.List<AltosDevice>     bt_devices = AltosBTKnown.bt_known().list(product);
+               num_devices += bt_devices.size();
+               AltosDevice[]                   devices = new AltosDevice[num_devices];
+
+               for (int i = 0; i < usb_devices.size(); i++)
+                       devices[i] = usb_devices.get(i);
+               int off = usb_devices.size();
+               for (int j = 0; j < bt_devices.size(); j++)
+                       devices[off + j] = bt_devices.get(j);
+               return devices;
+       }
+
+       private void update_devices() {
+               AltosDevice[] devices = devices();
+               list.setListData(devices);
+               select_button.setEnabled(devices.length > 0);
+       }
+
+       private AltosDeviceDialog (Frame in_frame, Component location, int in_product) {
+               super(in_frame, "Device Selection", true);
+
+               product = in_product;
+               frame = in_frame;
+               value = null;
+
+               AltosDevice[]   devices = devices();
+
+               cancel_button = new JButton("Cancel");
+               cancel_button.setActionCommand("cancel");
+               cancel_button.addActionListener(this);
+
+               manage_bluetooth_button = new JButton("Manage Bluetooth");
+               manage_bluetooth_button.setActionCommand("manage");
+               manage_bluetooth_button.addActionListener(this);
+
+               select_button = new JButton("Select");
+               select_button.setActionCommand("select");
+               select_button.addActionListener(this);
+               if (devices.length == 0)
+                       select_button.setEnabled(false);
+               getRootPane().setDefaultButton(select_button);
+
+               list = new JList(devices) {
+                               //Subclass JList to workaround bug 4832765, which can cause the
+                               //scroll pane to not let the user easily scroll up to the beginning
+                               //of the list.  An alternative would be to set the unitIncrement
+                               //of the JScrollBar to a fixed value. You wouldn't get the nice
+                               //aligned scrolling, but it should work.
+                               public int getScrollableUnitIncrement(Rectangle visibleRect,
+                                                                     int orientation,
+                                                                     int direction) {
+                                       int row;
+                                       if (orientation == SwingConstants.VERTICAL &&
+                                           direction < 0 && (row = getFirstVisibleIndex()) != -1) {
+                                               Rectangle r = getCellBounds(row, row);
+                                               if ((r.y == visibleRect.y) && (row != 0))  {
+                                                       Point loc = r.getLocation();
+                                                       loc.y--;
+                                                       int prevIndex = locationToIndex(loc);
+                                                       Rectangle prevR = getCellBounds(prevIndex, prevIndex);
+
+                                                       if (prevR == null || prevR.y >= r.y) {
+                                                               return 0;
+                                                       }
+                                                       return prevR.height;
+                                               }
+                                       }
+                                       return super.getScrollableUnitIncrement(
+                                               visibleRect, orientation, direction);
+                               }
+                       };
+
+               list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
+               list.setVisibleRowCount(-1);
+               list.addMouseListener(new MouseAdapter() {
+                                public void mouseClicked(MouseEvent e) {
+                                        if (e.getClickCount() == 2) {
+                                                select_button.doClick(); //emulate button click
+                                        }
+                                }
+                       });
+               JScrollPane listScroller = new JScrollPane(list);
+               listScroller.setPreferredSize(new Dimension(400, 80));
+               listScroller.setAlignmentX(LEFT_ALIGNMENT);
+
+               //Create a container so that we can add a title around
+               //the scroll pane.  Can't add a title directly to the
+               //scroll pane because its background would be white.
+               //Lay out the label and scroll pane from top to bottom.
+               JPanel listPane = new JPanel();
+               listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
+
+               JLabel label = new JLabel("Select Device");
+               label.setLabelFor(list);
+               listPane.add(label);
+               listPane.add(Box.createRigidArea(new Dimension(0,5)));
+               listPane.add(listScroller);
+               listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+               //Lay out the buttons from left to right.
+               JPanel buttonPane = new JPanel();
+               buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS));
+               buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
+               buttonPane.add(Box.createHorizontalGlue());
+               buttonPane.add(cancel_button);
+               buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+               buttonPane.add(manage_bluetooth_button);
+               buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+               buttonPane.add(select_button);
+
+               //Put everything together, using the content pane's BorderLayout.
+               Container contentPane = getContentPane();
+               contentPane.add(listPane, BorderLayout.CENTER);
+               contentPane.add(buttonPane, BorderLayout.PAGE_END);
+
+               //Initialize values.
+               if (devices != null && devices.length != 0)
+                       list.setSelectedValue(devices[0], true);
+               pack();
+               setLocationRelativeTo(location);
+       }
+
+       //Handle clicks on the Set and Cancel buttons.
+       public void actionPerformed(ActionEvent e) {
+               if ("select".equals(e.getActionCommand()))
+                       value = (AltosDevice)(list.getSelectedValue());
+               if ("manage".equals(e.getActionCommand())) {
+                       AltosBTManage.show(frame, AltosBTKnown.bt_known());
+                       update_devices();
+                       return;
+               }
+               setVisible(false);
+       }
+
+       public static AltosDevice show (Component frameComp, int product) {
+
+               Frame                           frame = JOptionPane.getFrameForComponent(frameComp);
+               AltosDeviceDialog       dialog;
+
+               dialog = new AltosDeviceDialog(frame, frameComp, product);
+               dialog.setVisible(true);
+               return dialog.getValue();
+       }
+}
diff --git a/altosui/AltosDialog.java b/altosui/AltosDialog.java
new file mode 100644 (file)
index 0000000..ff38c3e
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+class AltosDialogListener extends WindowAdapter {
+       public void windowClosing (WindowEvent e) {
+               AltosUIPreferences.unregister_ui_listener((AltosDialog) e.getWindow());
+       }
+}
+
+public class AltosDialog extends JDialog implements AltosUIListener {
+
+       public void ui_changed(String look_and_feel) {
+               SwingUtilities.updateComponentTreeUI(this);
+               this.pack();
+       }
+
+       public AltosDialog() {
+               AltosUIPreferences.register_ui_listener(this);
+               addWindowListener(new AltosDialogListener());
+       }
+
+       public AltosDialog(Frame frame, String label, boolean modal) {
+               super(frame, label, modal);
+               AltosUIPreferences.register_ui_listener(this);
+               addWindowListener(new AltosDialogListener());
+       }
+
+       public AltosDialog(Frame frame, boolean modal) {
+               super(frame, modal);
+               AltosUIPreferences.register_ui_listener(this);
+               addWindowListener(new AltosDialogListener());
+       }
+}
diff --git a/altosui/AltosDisplayThread.java b/altosui/AltosDisplayThread.java
new file mode 100644 (file)
index 0000000..cf69c41
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosDisplayThread extends Thread {
+
+       Frame                   parent;
+       IdleThread              idle_thread;
+       AltosVoice              voice;
+       AltosFlightReader       reader;
+       int                     crc_errors;
+       AltosFlightDisplay      display;
+
+       void show_internal(AltosState state, int crc_errors) {
+               if (state != null)
+                       display.show(state, crc_errors);
+       }
+
+       void show_safely(AltosState in_state, int in_crc_errors) {
+               final AltosState state = in_state;
+               final int crc_errors = in_crc_errors;
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               show_internal(state, crc_errors);
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       void reading_error_internal() {
+               JOptionPane.showMessageDialog(parent,
+                                             String.format("Error reading from \"%s\"", reader.name),
+                                             "Telemetry Read Error",
+                                             JOptionPane.ERROR_MESSAGE);
+       }
+
+       void reading_error_safely() {
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               reading_error_internal();
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       class IdleThread extends Thread {
+
+               boolean started;
+               private AltosState state;
+               int     reported_landing;
+               int     report_interval;
+               long    report_time;
+
+               public synchronized void report(boolean last) {
+                       if (state == null)
+                               return;
+
+                       /* reset the landing count once we hear about a new flight */
+                       if (state.state < Altos.ao_flight_drogue)
+                               reported_landing = 0;
+
+                       /* Shut up once the rocket is on the ground */
+                       if (reported_landing > 2) {
+                               return;
+                       }
+
+                       /* If the rocket isn't on the pad, then report height */
+                       if (Altos.ao_flight_drogue <= state.state &&
+                           state.state < Altos.ao_flight_landed &&
+                           state.range >= 0)
+                       {
+                               voice.speak("Height %s, bearing %s %d, elevation %d, range %s.\n",
+                                           AltosConvert.height.say(state.height),
+                                           state.from_pad.bearing_words(
+                                                   AltosGreatCircle.BEARING_VOICE),
+                                           (int) (state.from_pad.bearing + 0.5),
+                                           (int) (state.elevation + 0.5),
+                                           AltosConvert.distance.say(state.range));
+                       } else if (state.state > Altos.ao_flight_pad) {
+                               voice.speak(AltosConvert.height.say_units(state.height));
+                       } else {
+                               reported_landing = 0;
+                       }
+
+                       /* If the rocket is coming down, check to see if it has landed;
+                        * either we've got a landed report or we haven't heard from it in
+                        * a long time
+                        */
+                       if (state.state >= Altos.ao_flight_drogue &&
+                           (last ||
+                            System.currentTimeMillis() - state.report_time >= 15000 ||
+                            state.state == Altos.ao_flight_landed))
+                       {
+                               if (Math.abs(state.baro_speed) < 20 && state.height < 100)
+                                       voice.speak("rocket landed safely");
+                               else
+                                       voice.speak("rocket may have crashed");
+                               if (state.from_pad != null)
+                                       voice.speak("Bearing %d degrees, range %s.",
+                                                   (int) (state.from_pad.bearing + 0.5),
+                                                   AltosConvert.distance.say_units(state.from_pad.distance));
+                               ++reported_landing;
+                               if (state.state != Altos.ao_flight_landed) {
+                                       state.state = Altos.ao_flight_landed;
+                                       show_safely(state, 0);
+                               }
+                       }
+               }
+
+               long now () {
+                       return System.currentTimeMillis();
+               }
+
+               void set_report_time() {
+                       report_time = now() + report_interval;
+               }
+
+               public void run () {
+                       try {
+                               for (;;) {
+                                       set_report_time();
+                                       for (;;) {
+                                               voice.drain();
+                                               synchronized (this) {
+                                                       long    sleep_time = report_time - now();
+                                                       if (sleep_time <= 0)
+                                                               break;
+                                                       wait(sleep_time);
+                                               }
+                                       }
+                                       report(false);
+                               }
+                       } catch (InterruptedException ie) {
+                               try {
+                                       voice.drain();
+                               } catch (InterruptedException iie) { }
+                       }
+               }
+
+               public synchronized void notice(AltosState new_state, boolean spoken) {
+                       AltosState old_state = state;
+                       state = new_state;
+                       if (!started && state.state > Altos.ao_flight_pad) {
+                               started = true;
+                               start();
+                       }
+
+                       if (state.state < Altos.ao_flight_drogue)
+                               report_interval = 10000;
+                       else
+                               report_interval = 20000;
+                       if (old_state != null && old_state.state != state.state) {
+                               report_time = now();
+                               this.notify();
+                       } else if (spoken)
+                               set_report_time();
+               }
+
+               public IdleThread() {
+                       state = null;
+                       reported_landing = 0;
+                       report_interval = 10000;
+               }
+       }
+
+       boolean tell(AltosState state, AltosState old_state) {
+               boolean ret = false;
+               if (old_state == null || old_state.state != state.state) {
+                       voice.speak(state.data.state());
+                       if ((old_state == null || old_state.state <= Altos.ao_flight_boost) &&
+                           state.state > Altos.ao_flight_boost) {
+                               voice.speak("max speed: %s.",
+                                           AltosConvert.speed.say_units(state.max_speed + 0.5));
+                               ret = true;
+                       } else if ((old_state == null || old_state.state < Altos.ao_flight_drogue) &&
+                                  state.state >= Altos.ao_flight_drogue) {
+                               voice.speak("max height: %s.",
+                                           AltosConvert.height.say_units(state.max_height + 0.5));
+                               ret = true;
+                       }
+               }
+               if (old_state == null || old_state.gps_ready != state.gps_ready) {
+                       if (state.gps_ready) {
+                               voice.speak("GPS ready");
+                               ret = true;
+                       }
+                       else if (old_state != null) {
+                               voice.speak("GPS lost");
+                               ret = true;
+                       }
+               }
+               old_state = state;
+               return ret;
+       }
+
+       public void run() {
+               boolean         interrupted = false;
+               String          line;
+               AltosState      state = null;
+               AltosState      old_state = null;
+               boolean         told;
+
+               idle_thread = new IdleThread();
+
+               try {
+                       for (;;) {
+                               try {
+                                       AltosRecord record = reader.read();
+                                       if (record == null)
+                                               break;
+                                       old_state = state;
+                                       state = new AltosState(record, state);
+                                       reader.update(state);
+                                       show_safely(state, crc_errors);
+                                       told = tell(state, old_state);
+                                       idle_thread.notice(state, told);
+                               } catch (ParseException pp) {
+                                       System.out.printf("Parse error: %d \"%s\"\n", pp.getErrorOffset(), pp.getMessage());
+                               } catch (AltosCRCException ce) {
+                                       ++crc_errors;
+                                       show_safely(state, crc_errors);
+                               }
+                       }
+               } catch (InterruptedException ee) {
+                       interrupted = true;
+               } catch (IOException ie) {
+                       reading_error_safely();
+               } finally {
+                       if (!interrupted)
+                               idle_thread.report(true);
+                       reader.close(interrupted);
+                       idle_thread.interrupt();
+                       try {
+                               idle_thread.join();
+                       } catch (InterruptedException ie) {}
+               }
+       }
+
+       public AltosDisplayThread(Frame in_parent, AltosVoice in_voice, AltosFlightDisplay in_display, AltosFlightReader in_reader) {
+               parent = in_parent;
+               voice = in_voice;
+               display = in_display;
+               reader = in_reader;
+               display.reset();
+       }
+}
diff --git a/altosui/AltosEepromDelete.java b/altosui/AltosEepromDelete.java
new file mode 100644 (file)
index 0000000..73f3a00
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosEepromDelete implements Runnable {
+       AltosEepromList         flights;
+       Thread                  eeprom_thread;
+       AltosSerial             serial_line;
+       boolean                 remote;
+       JFrame                  frame;
+       ActionListener          listener;
+       boolean                 success;
+
+       private void DeleteLog (AltosEepromLog log)
+               throws IOException, InterruptedException, TimeoutException {
+
+               if (flights.config_data.flight_log_max != 0 || flights.config_data.log_format != 0) {
+
+                       /* Devices with newer firmware can erase the
+                        * flash blocks containing each flight
+                        */
+                       serial_line.flush_input();
+                       serial_line.printf("d %d\n", log.flight);
+                       for (;;) {
+                               /* It can take a while to erase the flash... */
+                               String line = serial_line.get_reply(20000);
+                               if (line == null)
+                                       throw new TimeoutException();
+                               if (line.equals("Erased"))
+                                       break;
+                               if (line.startsWith("No such"))
+                                       throw new IOException(line);
+                       }
+               }
+       }
+
+       private void show_error_internal(String message, String title) {
+               JOptionPane.showMessageDialog(frame,
+                                             message,
+                                             title,
+                                             JOptionPane.ERROR_MESSAGE);
+       }
+
+       private void show_error(String in_message, String in_title) {
+               final String message = in_message;
+               final String title = in_title;
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               show_error_internal(message, title);
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       public void run () {
+               success = false;
+               try {
+                       if (remote)
+                               serial_line.start_remote();
+
+                       for (AltosEepromLog log : flights) {
+                               if (log.selected) {
+                                       DeleteLog(log);
+                               }
+                       }
+                       success = true;
+               } catch (IOException ee) {
+                       show_error (ee.getLocalizedMessage(),
+                                   serial_line.device.toShortString());
+               } catch (InterruptedException ie) {
+               } catch (TimeoutException te) {
+                       show_error (String.format("Connection to \"%s\" failed",
+                                                 serial_line.device.toShortString()),
+                                   "Connection Failed");
+               } finally {
+                       try {
+                               if (remote)
+                                       serial_line.stop_remote();
+                       } catch (InterruptedException ie) {
+                       } finally {
+                               serial_line.flush_output();
+                               serial_line.close();
+                       }
+               }
+               if (listener != null) {
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               try {
+                                                       listener.actionPerformed(new ActionEvent(this,
+                                                                                                success ? 1 : 0,
+                                                                                                "delete"));
+                                               } catch (Exception ex) {
+                                               }
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+       }
+
+       public void start() {
+               eeprom_thread = new Thread(this);
+               eeprom_thread.start();
+       }
+
+       public void addActionListener(ActionListener l) {
+               listener = l;
+       }
+
+       public AltosEepromDelete(JFrame given_frame,
+                                AltosSerial given_serial_line,
+                                boolean given_remote,
+                                AltosEepromList given_flights) {
+               frame = given_frame;
+               serial_line = given_serial_line;
+               remote = given_remote;
+               flights = given_flights;
+               success = false;
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosEepromDownload.java b/altosui/AltosEepromDownload.java
new file mode 100644 (file)
index 0000000..b04890c
--- /dev/null
@@ -0,0 +1,495 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosEepromDownload implements Runnable {
+
+       JFrame                  frame;
+       AltosSerial             serial_line;
+       boolean                 remote;
+       Thread                  eeprom_thread;
+       AltosEepromMonitor      monitor;
+
+       int                     flight;
+       int                     serial;
+       int                     year, month, day;
+       boolean                 want_file;
+       FileWriter              eeprom_file;
+       LinkedList<String>      eeprom_pending;
+
+       AltosEepromList         flights;
+       ActionListener          listener;
+       boolean                 success;
+       ParseException          parse_exception;
+       String                  extension;
+
+       private void FlushPending() throws IOException {
+               for (String s : flights.config_data) {
+                       eeprom_file.write(s);
+                       eeprom_file.write('\n');
+               }
+
+               for (String s : eeprom_pending)
+                       eeprom_file.write(s);
+       }
+
+       private void CheckFile(boolean force) throws IOException {
+               if (eeprom_file != null)
+                       return;
+               if (force || (flight != 0 && want_file)) {
+                       AltosFile               eeprom_name;
+
+                       if (extension == null)
+                               extension = "data";
+                       if (year != 0 && month != 0 && day != 0)
+                               eeprom_name = new AltosFile(year, month, day, serial, flight, extension);
+                       else
+                               eeprom_name = new AltosFile(serial, flight, extension);
+
+                       eeprom_file = new FileWriter(eeprom_name);
+                       if (eeprom_file != null) {
+                               monitor.set_file(eeprom_name.getName());
+                               FlushPending();
+                               eeprom_pending = null;
+                       }
+               }
+       }
+
+       void Log(AltosEepromRecord r) throws IOException {
+               if (r.cmd != Altos.AO_LOG_INVALID) {
+                       String log_line = String.format("%c %4x %4x %4x\n",
+                                                       r.cmd, r.tick, r.a, r.b);
+                       if (eeprom_file != null)
+                               eeprom_file.write(log_line);
+                       else
+                               eeprom_pending.add(log_line);
+               }
+       }
+
+       void set_serial(int in_serial) {
+               serial = in_serial;
+               monitor.set_serial(serial);
+       }
+
+       void set_flight(int in_flight) {
+               flight = in_flight;
+               monitor.set_flight(flight);
+       }
+               
+       boolean                 done;
+       int                     state;
+
+       void CaptureFull(AltosEepromChunk eechunk) throws IOException {
+               boolean any_valid = false;
+
+               extension = "eeprom";
+               set_serial(flights.config_data.serial);
+               for (int i = 0; i < eechunk.chunk_size && !done; i += AltosEepromRecord.record_length) {
+                       try {
+                               AltosEepromRecord r = new AltosEepromRecord(eechunk, i);
+                               if (r.cmd == Altos.AO_LOG_FLIGHT)
+                                       set_flight(r.b);
+
+                               /* Monitor state transitions to update display */
+                               if (r.cmd == Altos.AO_LOG_STATE && r.a <= Altos.ao_flight_landed) {
+                                       state = r.a;
+                                       if (state > Altos.ao_flight_pad)
+                                               want_file = true;
+                               }
+
+                               if (r.cmd == Altos.AO_LOG_GPS_DATE) {
+                                       year = 2000 + (r.a & 0xff);
+                                       month = (r.a >> 8) & 0xff;
+                                       day = (r.b & 0xff);
+                                       want_file = true;
+                               }
+                               if (r.cmd == Altos.AO_LOG_STATE && r.a == Altos.ao_flight_landed)
+                                       done = true;
+                               any_valid = true;
+                               Log(r);
+                       } catch (ParseException pe) {
+                               if (parse_exception == null)
+                                       parse_exception = pe;
+                       }
+               }
+
+               if (!any_valid)
+                       done = true;
+
+               CheckFile(false);
+       }
+
+       boolean start;
+       int     tiny_tick;
+
+       void CaptureTiny (AltosEepromChunk eechunk) throws IOException {
+               boolean any_valid = false;
+
+               extension = "eeprom";
+               set_serial(flights.config_data.serial);
+               for (int i = 0; i < eechunk.data.length && !done; i += 2) {
+                       int                     v = eechunk.data16(i);
+                       AltosEepromRecord       r;
+
+                       if (i == 0 && start) {
+                               tiny_tick = 0;
+                               start = false;
+                               set_flight(v);
+                               r = new AltosEepromRecord(Altos.AO_LOG_FLIGHT, tiny_tick, 0, v);
+                               any_valid = true;
+                       } else {
+                               int     s = v ^ 0x8000;
+
+                               if (Altos.ao_flight_startup <= s && s <= Altos.ao_flight_invalid) {
+                                       state = s;
+                                       r = new AltosEepromRecord(Altos.AO_LOG_STATE, tiny_tick, state, 0);
+                                       if (state == Altos.ao_flight_landed)
+                                               done = true;
+                                       state = s;
+                                       any_valid = true;
+                               } else {
+                                       if (v != 0xffff)
+                                               any_valid = true;
+
+                                       r = new AltosEepromRecord(Altos.AO_LOG_PRESSURE, tiny_tick, 0, v);
+
+                                       /*
+                                        * The flight software records ascent data every 100ms, and descent
+                                        * data every 1s.
+                                        */
+                                       if (state < Altos.ao_flight_drogue)
+                                               tiny_tick += 10;
+                                       else
+                                               tiny_tick += 100;
+                               }
+                       }
+                       Log(r);
+               }
+               CheckFile(false);
+               if (!any_valid)
+                       done = true;
+       }
+
+       void LogTeleScience(AltosEepromTeleScience r) throws IOException {
+               if (r.type != Altos.AO_LOG_INVALID) {
+                       String log_line = String.format("%c %4x %4x %d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d %5d\n",
+                                                       r.type, r.tick, r.tm_tick, r.tm_state,
+                                                       r.data[0], r.data[1], r.data[2], r.data[3], 
+                                                       r.data[4], r.data[5], r.data[6], r.data[7], 
+                                                       r.data[8], r.data[9], r.data[10], r.data[11]);
+                       if (eeprom_file != null)
+                               eeprom_file.write(log_line);
+                       else
+                               eeprom_pending.add(log_line);
+               }
+       }
+       
+       boolean telescience_start;
+
+       void CaptureTeleScience (AltosEepromChunk eechunk) throws IOException {
+               boolean any_valid = false;
+
+               extension = "science";
+               for (int i = 0; i < eechunk.chunk_size && !done; i += AltosEepromTeleScience.record_length) {
+                       try {
+                               AltosEepromTeleScience r = new AltosEepromTeleScience(eechunk, i);
+                               if (r.type == AltosEepromTeleScience.AO_LOG_TELESCIENCE_START) {
+                                       if (telescience_start) {
+                                               done = true;
+                                               break;
+                                       }
+                                       set_serial(r.data[0]);
+                                       set_flight(r.data[1]);
+                                       telescience_start = true;
+                               } else {
+                                       if (!telescience_start)
+                                               break;
+                               }
+                               state = r.tm_state;
+                               want_file =true;
+                               any_valid = true;
+                               LogTeleScience(r);
+                       } catch (ParseException pe) {
+                               if (parse_exception == null)
+                                       parse_exception = pe;
+                       }
+               }
+
+               CheckFile(false);
+               if (!any_valid)
+                       done = true;
+       }
+
+       void LogMega(AltosEepromMega r) throws IOException {
+               if (r.cmd != Altos.AO_LOG_INVALID) {
+                       String log_line = String.format("%c %4x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x %2x\n",
+                                                       r.cmd, r.tick,
+                                                       r.data8[0], r.data8[1], r.data8[2], r.data8[3],
+                                                       r.data8[4], r.data8[5], r.data8[6], r.data8[7],
+                                                       r.data8[8], r.data8[9], r.data8[10], r.data8[11],
+                                                       r.data8[12], r.data8[13], r.data8[14], r.data8[15],
+                                                       r.data8[16], r.data8[17], r.data8[18], r.data8[19],
+                                                       r.data8[20], r.data8[21], r.data8[22], r.data8[23],
+                                                       r.data8[24], r.data8[25], r.data8[26], r.data8[27]);
+                       if (eeprom_file != null)
+                               eeprom_file.write(log_line);
+                       else
+                               eeprom_pending.add(log_line);
+               }
+       }
+
+       void CaptureMega(AltosEepromChunk eechunk) throws IOException {
+               boolean any_valid = false;
+
+               extension = "mega";
+               set_serial(flights.config_data.serial);
+               for (int i = 0; i < eechunk.chunk_size && !done; i += AltosEepromMega.record_length) {
+                       try {
+                               AltosEepromMega r = new AltosEepromMega(eechunk, i);
+                               if (r.cmd == Altos.AO_LOG_FLIGHT)
+                                       set_flight(r.data16(0));
+
+                               /* Monitor state transitions to update display */
+                               if (r.cmd == Altos.AO_LOG_STATE && r.data16(0) <= Altos.ao_flight_landed) {
+                                       state = r.data16(0);
+                                       if (state > Altos.ao_flight_pad)
+                                               want_file = true;
+                               }
+
+                               if (r.cmd == Altos.AO_LOG_GPS_TIME) {
+                                       year = 2000 + r.data8(14);
+                                       month = r.data8(15);
+                                       day = r.data8(14);
+                                       want_file = true;
+                               }
+
+                               if (r.cmd == Altos.AO_LOG_STATE && r.data16(0) == Altos.ao_flight_landed)
+                                       done = true;
+                               any_valid = true;
+                               LogMega(r);
+                       } catch (ParseException pe) {
+                               if (parse_exception == null)
+                                       parse_exception = pe;
+                       }
+               }
+               if (!any_valid)
+                       done = true;
+
+               CheckFile(false);
+       }
+       
+       void CaptureTelemetry(AltosEepromChunk eechunk) throws IOException {
+               
+       }
+
+       void CaptureLog(AltosEepromLog log) throws IOException, InterruptedException, TimeoutException {
+               int                     block, state_block = 0;
+               int                     log_format = flights.config_data.log_format;
+
+               state = 0;
+               done = false;
+               start = true;
+
+               if (flights.config_data.serial == 0)
+                       throw new IOException("no serial number found");
+
+               /* Reset per-capture variables */
+               flight = 0;
+               year = 0;
+               month = 0;
+               day = 0;
+               want_file = false;
+               eeprom_file = null;
+               eeprom_pending = new LinkedList<String>();
+
+               /* Set serial number in the monitor dialog window */
+               /* Now scan the eeprom, reading blocks of data and converting to .eeprom file form */
+
+               state = 0; state_block = log.start_block;
+               for (block = log.start_block; !done && block < log.end_block; block++) {
+                       monitor.set_value(AltosLib.state_name(state), state, block - state_block);
+
+                       AltosEepromChunk        eechunk = new AltosEepromChunk(serial_line, block, block == log.start_block);
+
+                       /*
+                        * Guess what kind of data is there if the device
+                        * didn't tell us
+                        */
+
+                       if (log_format == Altos.AO_LOG_FORMAT_UNKNOWN) {
+                               if (block == log.start_block) {
+                                       if (eechunk.data(0) == Altos.AO_LOG_FLIGHT)
+                                               log_format = Altos.AO_LOG_FORMAT_FULL;
+                                       else
+                                               log_format = Altos.AO_LOG_FORMAT_TINY;
+                               }
+                       }
+
+                       switch (log_format) {
+                       case AltosLib.AO_LOG_FORMAT_FULL:
+                               extension = "eeprom";
+                               CaptureFull(eechunk);
+                               break;
+                       case AltosLib.AO_LOG_FORMAT_TINY:
+                               extension = "eeprom";
+                               CaptureTiny(eechunk);
+                               break;
+                       case AltosLib.AO_LOG_FORMAT_TELEMETRY:
+                               extension = "telem";
+                               CaptureTelemetry(eechunk);
+                               break;
+                       case AltosLib.AO_LOG_FORMAT_TELESCIENCE:
+                               extension = "science";
+                               CaptureTeleScience(eechunk);
+                               break;
+                       case AltosLib.AO_LOG_FORMAT_MEGAMETRUM:
+                               extension = "mega";
+                               CaptureMega(eechunk);
+                       }
+               }
+               CheckFile(true);
+               if (eeprom_file != null) {
+                       eeprom_file.flush();
+                       eeprom_file.close();
+               }
+       }
+
+       private void show_message_internal(String message, String title, int message_type) {
+               JOptionPane.showMessageDialog(frame,
+                                             message,
+                                             title,
+                                             message_type);
+       }
+
+       private void show_message(String in_message, String in_title, int in_message_type) {
+               final String message = in_message;
+               final String title = in_title;
+               final int message_type = in_message_type;
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               show_message_internal(message, title, message_type);
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       public void run () {
+               try {
+                       boolean failed = false;
+                       if (remote)
+                               serial_line.start_remote();
+
+                       for (AltosEepromLog log : flights) {
+                               parse_exception = null;
+                               if (log.selected) {
+                                       monitor.reset();
+                                       CaptureLog(log);
+                               }
+                               if (parse_exception != null) {
+                                       failed = true;
+                                       show_message(String.format("Flight %d download error\n%s\nValid log data saved",
+                                                                  log.flight,
+                                                                  parse_exception.getMessage()),
+                                                    serial_line.device.toShortString(),
+                                                    JOptionPane.WARNING_MESSAGE);
+                               }
+                       }
+                       success = !failed;
+               } catch (IOException ee) {
+                       show_message(ee.getLocalizedMessage(),
+                                    serial_line.device.toShortString(),
+                                    JOptionPane.ERROR_MESSAGE);
+               } catch (InterruptedException ie) {
+                       System.out.printf("download interrupted\n");
+               } catch (TimeoutException te) {
+                       show_message(String.format("Connection to \"%s\" failed",
+                                                  serial_line.device.toShortString()),
+                                    "Connection Failed",
+                                    JOptionPane.ERROR_MESSAGE);
+               } finally {
+                       if (remote) {
+                               try {
+                                       serial_line.stop_remote();
+                               } catch (InterruptedException ie) {
+                               }
+                       }
+                       serial_line.flush_output();
+               }
+               monitor.done();
+               if (listener != null) {
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               try {
+                                                       listener.actionPerformed(new ActionEvent(this,
+                                                                                                success ? 1 : 0,
+                                                                                                "download"));
+                                               } catch (Exception ex) {
+                                               }
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+       }
+
+       public void start() {
+               eeprom_thread = new Thread(this);
+               eeprom_thread.start();
+       }
+
+       public void addActionListener(ActionListener l) {
+               listener = l;
+       }
+
+       public AltosEepromDownload(JFrame given_frame,
+                                  AltosSerial given_serial_line,
+                                  boolean given_remote,
+                                  AltosEepromList given_flights) {
+
+               frame = given_frame;
+               serial_line = given_serial_line;
+               serial_line.set_frame(frame);
+               remote = given_remote;
+               flights = given_flights;
+               success = false;
+
+               monitor = new AltosEepromMonitor(frame, Altos.ao_flight_boost, Altos.ao_flight_landed);
+               monitor.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       if (eeprom_thread != null)
+                                               eeprom_thread.interrupt();
+                               }
+                       });
+       }
+}
diff --git a/altosui/AltosEepromList.java b/altosui/AltosEepromList.java
new file mode 100644 (file)
index 0000000..6a65621
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+/*
+ * Temporary structure to hold the list of stored flights;
+ * each of these will be queried in turn to generate more
+ * complete information
+ */
+
+class AltosEepromFlight {
+       int     flight;
+       int     start;
+       int     end;
+
+       public AltosEepromFlight(int in_flight, int in_start, int in_end) {
+               flight = in_flight;
+               start = in_start;
+               end = in_end;
+       }
+}
+
+/*
+ * Construct a list of flights available in a connected device
+ */
+
+public class AltosEepromList extends ArrayList<AltosEepromLog> {
+       AltosConfigData config_data;
+
+       public AltosEepromList (AltosSerial serial_line, boolean remote)
+               throws IOException, InterruptedException, TimeoutException
+       {
+               try {
+                       if (remote)
+                               serial_line.start_remote();
+                       config_data = new AltosConfigData (serial_line);
+//                     if (config_data.serial == 0)
+//                             throw new IOException("no serial number found");
+
+                       ArrayList<AltosEepromFlight> flights = new ArrayList<AltosEepromFlight>();
+
+                       if (config_data.flight_log_max != 0 || config_data.log_format != 0) {
+
+                               /* Devices with newer firmware will support the 'l'
+                                * command which will list the region of storage
+                                * occupied by each available flight
+                                */
+                               serial_line.printf("l\n");
+                               for (;;) {
+                                       String line = serial_line.get_reply(5000);
+                                       if (line == null)
+                                               throw new TimeoutException();
+                                       if (line.contains("done"))
+                                               break;
+                                       if (line.contains("Syntax"))
+                                               continue;
+                                       String[] tokens = line.split("\\s+");
+                                       if (tokens.length < 6)
+                                               break;
+
+                                       int     flight = -1, start = -1, end = -1;
+                                       try {
+                                               if (tokens[0].equals("flight"))
+                                                       flight = AltosParse.parse_int(tokens[1]);
+                                               if (tokens[2].equals("start"))
+                                                       start = AltosParse.parse_hex(tokens[3]);
+                                               if (tokens[4].equals("end"))
+                                                       end = AltosParse.parse_hex(tokens[5]);
+                                               if (flight > 0 && start >= 0 && end > 0)
+                                                       flights.add(new AltosEepromFlight(flight, start, end));
+                                       } catch (ParseException pe) { System.out.printf("Parse error %s\n", pe.toString()); }
+                               }
+                       } else {
+
+                               /* Older devices will hold only a single
+                                * flight. This also assumes that any older
+                                * device will have a 1MB flash device
+                                */
+                               flights.add(new AltosEepromFlight(0, 0, 0xfff));
+                       }
+
+                       /* With the list of flights collected, collect more complete
+                        * information on them by reading the first block or two of
+                        * data. This will add GPS coordinates and a date. For older
+                        * firmware, this will also extract the flight number.
+                        */
+                       for (AltosEepromFlight flight : flights) {
+                               add(new AltosEepromLog(config_data, serial_line,
+                                                      flight.flight, flight.start, flight.end));
+                       }
+               } finally {
+                       if (remote)
+                               serial_line.stop_remote();
+                       serial_line.flush_output();
+               }
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosEepromManage.java b/altosui/AltosEepromManage.java
new file mode 100644 (file)
index 0000000..563c90b
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosEepromManage implements ActionListener {
+
+       JFrame                  frame;
+       boolean                 remote;
+       AltosDevice             device;
+       AltosSerial             serial_line;
+       AltosEepromList         flights;
+       AltosEepromDownload     download;
+       AltosEepromDelete       delete;
+
+       public void finish() {
+               if (serial_line != null) {
+                       try {
+                               serial_line.flush_input();
+                       } catch (InterruptedException ie) {
+                       }
+                       serial_line.close();
+                       serial_line = null;
+               }
+       }
+
+       private String showDeletedFlights() {
+               String  result = "";
+
+               for (AltosEepromLog flight : flights) {
+                       if (flight.selected) {
+                               if (result.equals(""))
+                                       result = String.format("%d", flight.flight);
+                               else
+                                       result = String.format("%s, %d", result, flight.flight);
+                       }
+               }
+               return result;
+       }
+
+       public boolean download_done() {
+               AltosEepromSelect       select = new AltosEepromSelect(frame, flights, "Delete");
+
+               if (select.run()) {
+                       boolean any_selected = false;
+                       for (AltosEepromLog flight : flights)
+                               any_selected = any_selected || flight.selected;
+                       if (any_selected) {
+                               delete = new AltosEepromDelete(frame,
+                                                              serial_line,
+                                                              remote,
+                                                              flights);
+                               delete.addActionListener(this);
+                               /*
+                                * Start flight log delete
+                                */
+
+                               delete.start();
+                               return true;
+                       }
+               }
+               return false;
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+               boolean success = e.getID() != 0;
+               boolean running = false;
+
+               if (cmd.equals("download")) {
+                       if (success)
+                               running = download_done();
+               } else if (cmd.equals("delete")) {
+                       if (success) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             String.format("Flights erased: %s",
+                                                                           showDeletedFlights()),
+                                                             serial_line.device.toShortString(),
+                                                             JOptionPane.INFORMATION_MESSAGE);
+                       }
+               }
+               if (!running)
+                       finish();
+       }
+
+       public void got_flights(AltosEepromList in_flights) {
+               boolean running = false;;
+
+               flights = in_flights;
+               try {
+                       if (flights.size() == 0) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             String.format("No flights available on %d",
+                                                                           device.getSerial()),
+                                                             serial_line.device.toShortString(),
+                                                             JOptionPane.INFORMATION_MESSAGE);
+                       } else {
+                               AltosEepromSelect       select = new AltosEepromSelect(frame, flights, "Download");
+
+                               if (select.run()) {
+                                       boolean any_selected = false;
+                                       for (AltosEepromLog flight : flights)
+                                               any_selected = any_selected || flight.selected;
+                                       if (any_selected) {
+                                               download = new AltosEepromDownload(frame,
+                                                                                  serial_line,
+                                                                                  remote,
+                                                                                  flights);
+                                               download.addActionListener(this);
+                                               /*
+                                                * Start flight log download
+                                                */
+
+                                               download.start();
+                                               running = true;
+                                       } else {
+                                               running = download_done();
+                                       }
+                               }
+                       }
+                       if (!running)
+                               finish();
+               } catch (Exception e) {
+                       got_exception(e);
+               }
+       }
+
+       public void got_exception(Exception e) {
+               if (e instanceof IOException) {
+                       IOException     ee = (IOException) e;
+                       JOptionPane.showMessageDialog(frame,
+                                                     device.toShortString(),
+                                                     ee.getLocalizedMessage(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof TimeoutException) {
+                       TimeoutException te = (TimeoutException) e;
+                       JOptionPane.showMessageDialog(frame,
+                                                     String.format("Communications failed with \"%s\"",
+                                                                   device.toShortString()),
+                                                     "Cannot open target device",
+                                                     JOptionPane.ERROR_MESSAGE);
+               }
+               finish();
+       }
+
+       class EepromGetList implements Runnable {
+
+               AltosEepromManage       manage;
+
+               public void run () {
+                       Runnable r;
+                       try {
+                               flights = new AltosEepromList(serial_line, remote);
+                               r = new Runnable() {
+                                               public void run() {
+                                                       got_flights(flights);
+                                               }
+                                       };
+                       } catch (Exception e) {
+                               final Exception f_e = e;
+                               r = new Runnable() {
+                                               public void run() {
+                                                       got_exception(f_e);
+                                               }
+                                       };
+                       }
+                       SwingUtilities.invokeLater(r);
+               }
+
+               public EepromGetList(AltosEepromManage in_manage) {
+                       manage = in_manage;
+               }
+       }
+
+       public AltosEepromManage(JFrame given_frame) {
+
+               boolean running = false;
+
+               frame = given_frame;
+               device = AltosDeviceDialog.show(frame, Altos.product_any);
+
+               remote = false;
+
+               if (device != null) {
+                       try {
+                               serial_line = new AltosSerial(device);
+                               if (device.matchProduct(Altos.product_basestation))
+                                       remote = true;
+
+                               serial_line.set_frame(frame);
+
+                               EepromGetList   get_list = new EepromGetList(this);
+                               Thread          t = new Thread(get_list);
+                               t.start();
+                       } catch (FileNotFoundException ee) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             ee.getMessage(),
+                                                             "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);
+                       }
+               }
+       }
+}
diff --git a/altosui/AltosEepromMonitor.java b/altosui/AltosEepromMonitor.java
new file mode 100644 (file)
index 0000000..7564344
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosEepromMonitor extends AltosDialog {
+
+       Container       pane;
+       Box             box;
+       JLabel          serial_label;
+       JLabel          flight_label;
+       JLabel          file_label;
+       JLabel          serial_value;
+       JLabel          flight_value;
+       JLabel          file_value;
+       JButton         cancel;
+       JProgressBar    pbar;
+       int             min_state, max_state;
+
+       public AltosEepromMonitor(JFrame owner, int in_min_state, int in_max_state) {
+               super (owner, "Download Flight Data", false);
+
+               GridBagConstraints c;
+               Insets il = new Insets(4,4,4,4);
+               Insets ir = new Insets(4,4,4,4);
+
+               pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 0;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               serial_label = new JLabel("Serial:");
+               pane.add(serial_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 1; c.gridy = 0;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               serial_value = new JLabel("");
+               pane.add(serial_value, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.NONE;
+               c.gridx = 0; c.gridy = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               flight_label = new JLabel("Flight:");
+               pane.add(flight_label, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.gridx = 1; c.gridy = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               flight_value = new JLabel("");
+               pane.add(flight_value, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.NONE;
+               c.gridx = 0; c.gridy = 2;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               file_label = new JLabel("File:");
+               pane.add(file_label, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.gridx = 1; c.gridy = 2;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               file_value = new JLabel("");
+               pane.add(file_value, c);
+
+               min_state = in_min_state;
+               max_state = in_max_state;
+               pbar = new JProgressBar();
+               pbar.setMinimum(0);
+               pbar.setMaximum((max_state - min_state) * 100);
+               pbar.setValue(0);
+               pbar.setString("startup");
+               pbar.setStringPainted(true);
+               pbar.setPreferredSize(new Dimension(600, 20));
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0; c.gridy = 3;
+               c.gridwidth = GridBagConstraints.REMAINDER;
+               Insets ib = new Insets(4,4,4,4);
+               c.insets = ib;
+               pane.add(pbar, c);
+
+
+               cancel = new JButton("Cancel");
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0; c.gridy = 4;
+               c.gridwidth = GridBagConstraints.REMAINDER;
+               Insets ic = new Insets(4,4,4,4);
+               c.insets = ic;
+               pane.add(cancel, c);
+
+               pack();
+               setLocationRelativeTo(owner);
+               setVisible(true);
+       }
+
+       public void addActionListener (ActionListener l) {
+               cancel.addActionListener(l);
+       }
+
+       private void set_value_internal(String state_name, int in_state, int in_block) {
+               int block = in_block;
+               int state = in_state;
+
+               if (block > 100)
+                       block = 100;
+               if (state < min_state) state = min_state;
+               if (state >= max_state) state = max_state - 1;
+               state -= min_state;
+
+               int pos = state * 100 + block;
+
+               pbar.setString(state_name);
+               pbar.setValue(pos);
+       }
+
+       public void set_value(String in_state_name, int in_state, int in_block) {
+               final String state_name = in_state_name;
+               final int state = in_state;
+               final int block = in_block;
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               set_value_internal(state_name, state, block);
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       private void set_serial_internal(int serial) {
+               serial_value.setText(String.format("%d", serial));
+       }
+
+       public void set_serial(int in_serial) {
+               final int serial = in_serial;
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               set_serial_internal(serial);
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       private void set_flight_internal(int flight) {
+               flight_value.setText(String.format("%d", flight));
+       }
+
+       public void set_flight(int in_flight) {
+               final int flight = in_flight;
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               set_flight_internal(flight);
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       private void set_file_internal(String file) {
+               file_value.setText(String.format("%s", file));
+       }
+
+       public void set_file(String in_file) {
+               final String file = in_file;
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               set_file_internal(file);
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       private void done_internal() {
+               setVisible(false);
+               dispose();
+       }
+
+       public void done() {
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               done_internal();
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       private void reset_internal() {
+               set_value_internal("startup",min_state,0);
+               set_flight_internal(0);
+               set_file_internal("");
+       }
+
+       public void reset() {
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       try {
+                                               reset_internal();
+                                       } catch (Exception ex) {
+                                       }
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+}
diff --git a/altosui/AltosEepromSelect.java b/altosui/AltosEepromSelect.java
new file mode 100644 (file)
index 0000000..4ad7889
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright © 2011 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.util.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import java.awt.*;
+import java.awt.event.*;
+import libaltosJNI.libaltos;
+import libaltosJNI.altos_device;
+import libaltosJNI.SWIGTYPE_p_altos_file;
+import libaltosJNI.SWIGTYPE_p_altos_list;
+import org.altusmetrum.AltosLib.*;
+
+class AltosEepromItem implements ActionListener {
+       AltosEepromLog  log;
+       JLabel          label;
+       JCheckBox       action;
+       JCheckBox       delete;
+
+       public void actionPerformed(ActionEvent e) {
+               log.selected = action.isSelected();
+       }
+
+       public AltosEepromItem(AltosEepromLog in_log) {
+               log = in_log;
+
+               String  text;
+               if (log.year != 0)
+                       text = String.format("Flight #%02d - %04d-%02d-%02d",
+                                            log.flight, log.year, log.month, log.day);
+               else
+                       text = String.format("Flight #%02d", log.flight);
+
+               label = new JLabel(text);
+
+               action = new JCheckBox("", log.selected);
+               action.addActionListener(this);
+       }
+}
+
+public class AltosEepromSelect extends AltosDialog implements ActionListener {
+       private JList                   list;
+       private JFrame                  frame;
+       JButton                         ok;
+       JButton                         cancel;
+       boolean                         success;
+
+       /* Listen for events from our buttons */
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("ok"))
+                       success = true;
+               setVisible(false);
+       }
+
+       public boolean run() {
+               success = false;
+               setLocationRelativeTo(frame);
+               setVisible(true);
+               return success;
+       }
+
+       public AltosEepromSelect (JFrame in_frame,
+                                 AltosEepromList flights,
+                                 String action) {
+
+               super(in_frame, String.format("Flight list for serial %d", flights.config_data.serial), true);
+               frame = in_frame;
+
+               /* Create the container for the dialog */
+               Container contentPane = getContentPane();
+
+               /* First, we create a pane containing the dialog's header/title */
+               JLabel  selectLabel = new JLabel(String.format ("Select flights to %s", action), SwingConstants.CENTER);
+
+               JPanel  labelPane = new JPanel();
+               labelPane.setLayout(new BoxLayout(labelPane, BoxLayout.X_AXIS));
+               labelPane.setBorder(BorderFactory.createEmptyBorder(10, 0, 10, 0));
+               labelPane.add(Box.createHorizontalGlue());
+               labelPane.add(selectLabel);
+               labelPane.add(Box.createHorizontalGlue());
+
+               /* Add the header to the container. */
+               contentPane.add(labelPane, BorderLayout.PAGE_START);
+
+
+               /* Now we create the evilness that is a GridBag for the flight details */
+               GridBagConstraints c;
+               Insets i = new Insets(4,4,4,4);
+               JPanel flightPane = new JPanel();
+               flightPane.setLayout(new GridBagLayout());
+               flightPane.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
+
+               /* Flight Header */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 0;
+               c.fill = GridBagConstraints.NONE;
+               c.weightx = 0.5;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               JLabel flightHeaderLabel = new JLabel("Flight");
+               flightPane.add(flightHeaderLabel, c);
+
+               /* Download Header */
+               c = new GridBagConstraints();
+               c.gridx = 1; c.gridy = 0;
+               c.fill = GridBagConstraints.NONE;
+               c.weightx = 0.5;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               JLabel downloadHeaderLabel = new JLabel(action);
+               flightPane.add(downloadHeaderLabel, c);
+
+               /* Add the flights to the GridBag */
+               AltosEepromItem item;
+               int itemNumber = 1;
+               for (AltosEepromLog flight : flights) {
+                       /* Create a flight object with handlers and
+                        * appropriate UI items
+                        */
+                       item = new AltosEepromItem(flight);
+
+                       /* Add a decriptive label for the flight */
+                       c = new GridBagConstraints();
+                       c.gridx = 0; c.gridy = itemNumber;
+                       c.fill = GridBagConstraints.NONE;
+                       c.weightx = 0.5;
+                       c.anchor = GridBagConstraints.CENTER;
+                       c.insets = i;
+                       flightPane.add(item.label, c);
+
+                       /* Add action checkbox for the flight */
+                       c = new GridBagConstraints();
+                       c.gridx = 1; c.gridy = itemNumber;
+                       c.fill = GridBagConstraints.NONE;
+                       c.weightx = 0.5;
+                       c.anchor = GridBagConstraints.CENTER;
+                       c.insets = i;
+                       flightPane.add(item.action, c);
+
+                       itemNumber++;
+               }
+
+               /* Add the GridBag to the container */
+               contentPane.add(flightPane, BorderLayout.CENTER);
+
+               /* Create the dialog buttons */
+               ok = new JButton("OK");
+               ok.addActionListener(this);
+               ok.setActionCommand("ok");
+
+               cancel = new JButton("Cancel");
+               cancel.addActionListener(this);
+               cancel.setActionCommand("cancel");
+
+               JPanel  buttonPane = new JPanel();
+               buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
+               buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+               buttonPane.add(Box.createHorizontalGlue());
+               buttonPane.add(cancel);
+               buttonPane.add(Box.createRigidArea(new Dimension(10, 0)));
+               buttonPane.add(ok);
+
+               /* Add the buttons to the container */
+               contentPane.add(buttonPane, BorderLayout.PAGE_END);
+
+               /* Pack the window! */
+               pack();
+       }
+}
diff --git a/altosui/AltosFlash.java b/altosui/AltosFlash.java
new file mode 100644 (file)
index 0000000..bd0c8a5
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosFlash {
+       File            file;
+       FileInputStream input;
+       AltosHexfile    image;
+       JFrame          frame;
+       AltosDevice     debug_dongle;
+       AltosDebug      debug;
+       AltosRomconfig  rom_config;
+       ActionListener  listener;
+       boolean         aborted;
+
+       static final byte MOV_direct_data       = (byte) 0x75;
+       static final byte MOV_DPTR_data16       = (byte) 0x90;
+       static final byte MOV_A_data            = (byte) 0x74;
+       static final byte MOVX_atDPTR_A         = (byte) 0xf0;
+       static final byte MOVX_A_atDPTR         = (byte) 0xe0;
+       static final byte INC_DPTR              = (byte) 0xa3;
+       static final byte TRAP                  = (byte) 0xa5;
+
+       static final byte JB                    = (byte) 0x20;
+
+       static final byte MOV_A_direct          = (byte) 0xe5;
+       static final byte MOV_direct1_direct2   = (byte) 0x85;
+       static final byte MOV_direct_A          = (byte) 0xf5;
+       static final byte MOV_R0_data           = (byte) (0x78 | 0);
+       static final byte MOV_R1_data           = (byte) (0x78 | 1);
+       static final byte MOV_R2_data           = (byte) (0x78 | 2);
+       static final byte MOV_R3_data           = (byte) (0x78 | 3);
+       static final byte MOV_R4_data           = (byte) (0x78 | 4);
+       static final byte MOV_R5_data           = (byte) (0x78 | 5);
+       static final byte MOV_R6_data           = (byte) (0x78 | 6);
+       static final byte MOV_R7_data           = (byte) (0x78 | 7);
+       static final byte DJNZ_R0_rel           = (byte) (0xd8 | 0);
+       static final byte DJNZ_R1_rel           = (byte) (0xd8 | 1);
+       static final byte DJNZ_R2_rel           = (byte) (0xd8 | 2);
+       static final byte DJNZ_R3_rel           = (byte) (0xd8 | 3);
+       static final byte DJNZ_R4_rel           = (byte) (0xd8 | 4);
+       static final byte DJNZ_R5_rel           = (byte) (0xd8 | 5);
+       static final byte DJNZ_R6_rel           = (byte) (0xd8 | 6);
+       static final byte DJNZ_R7_rel           = (byte) (0xd8 | 7);
+
+       static final byte P1DIR                 = (byte) 0xFE;
+       static final byte P1                    = (byte) 0x90;
+
+       /* flash controller */
+       static final byte FWT                   = (byte) 0xAB;
+       static final byte FADDRL                = (byte) 0xAC;
+       static final byte FADDRH                = (byte) 0xAD;
+       static final byte FCTL                  = (byte) 0xAE;
+       static final byte FCTL_BUSY             = (byte) 0x80;
+       static final byte FCTL_BUSY_BIT         = (byte) 7;
+       static final byte FCTL_SWBSY            = (byte) 0x40;
+       static final byte FCTL_SWBSY_BIT        = (byte) 6;
+       static final byte FCTL_CONTRD           = (byte) 0x10;
+       static final byte FCTL_WRITE            = (byte) 0x02;
+       static final byte FCTL_ERASE            = (byte) 0x01;
+       static final byte FWDATA                = (byte) 0xAF;
+
+       static final byte ACC                   = (byte) 0xE0;
+
+       /* offsets within the flash_page program */
+       static final int FLASH_ADDR_HIGH        = 8;
+       static final int FLASH_ADDR_LOW         = 11;
+       static final int RAM_ADDR_HIGH          = 13;
+       static final int RAM_ADDR_LOW           = 14;
+       static final int FLASH_WORDS_HIGH       = 16;
+       static final int FLASH_WORDS_LOW        = 18;
+       static final int FLASH_TIMING           = 21;
+
+       /* sleep mode control */
+       static final int SLEEP                  = (byte) 0xbe;
+       static final int  SLEEP_USB_EN          = (byte) 0x80;
+       static final int  SLEEP_XOSC_STB        = (byte) 0x40;
+       static final int  SLEEP_HFRC_STB        = (byte) 0x20;
+       static final int  SLEEP_RST_MASK        = (byte) 0x18;
+       static final int   SLEEP_RST_POWERON    = (byte) 0x00;
+       static final int   SLEEP_RST_EXTERNAL   = (byte) 0x10;
+       static final int   SLEEP_RST_WATCHDOG   = (byte) 0x08;
+       static final int  SLEEP_OSC_PD          = (byte) 0x04;
+       static final int  SLEEP_MODE_MASK       = (byte) 0x03;
+       static final int   SLEEP_MODE_PM0       = (byte) 0x00;
+       static final int   SLEEP_MODE_PM1       = (byte) 0x01;
+       static final int   SLEEP_MODE_PM2       = (byte) 0x02;
+       static final int   SLEEP_MODE_PM3       = (byte) 0x03;
+
+       /* clock controller */
+       static final byte CLKCON                = (byte) 0xC6;
+       static final byte  CLKCON_OSC32K        = (byte) 0x80;
+       static final byte  CLKCON_OSC           = (byte) 0x40;
+       static final byte  CLKCON_TICKSPD       = (byte) 0x38;
+       static final byte  CLKCON_CLKSPD        = (byte) 0x07;
+
+       static final byte[] flash_page_proto = {
+
+               MOV_direct_data, P1DIR, (byte) 0x02,
+               MOV_direct_data, P1,    (byte) 0xFF,
+
+               MOV_direct_data, FADDRH, 0,     /* FLASH_ADDR_HIGH */
+
+               MOV_direct_data, FADDRL, 0,     /* FLASH_ADDR_LOW */
+
+               MOV_DPTR_data16, 0, 0,          /* RAM_ADDR_HIGH, RAM_ADDR_LOW */
+
+               MOV_R7_data, 0,                 /* FLASH_WORDS_HIGH */
+
+               MOV_R6_data, 0,                 /* FLASH_WORDS_LOW */
+
+
+               MOV_direct_data, FWT, 0x20,     /* FLASH_TIMING */
+
+               MOV_direct_data, FCTL, FCTL_ERASE,
+/* eraseWaitLoop: */
+               MOV_A_direct,           FCTL,
+               JB, ACC|FCTL_BUSY_BIT, (byte) 0xfb,
+
+               MOV_direct_data, P1, (byte) 0xfd,
+
+               MOV_direct_data, FCTL, FCTL_WRITE,
+/* writeLoop: */
+               MOV_R5_data, 2,
+/* writeWordLoop: */
+               MOVX_A_atDPTR,
+               INC_DPTR,
+               MOV_direct_A, FWDATA,
+               DJNZ_R5_rel, (byte) 0xfa,               /* writeWordLoop */
+/* writeWaitLoop: */
+               MOV_A_direct, FCTL,
+               JB, ACC|FCTL_SWBSY_BIT, (byte) 0xfb,    /* writeWaitLoop */
+               DJNZ_R6_rel, (byte) 0xf1,               /* writeLoop */
+               DJNZ_R7_rel, (byte) 0xef,                       /* writeLoop */
+
+               MOV_direct_data, P1DIR, (byte) 0x00,
+               MOV_direct_data, P1,    (byte) 0xFF,
+               TRAP,
+       };
+
+       public byte[] make_flash_page(int flash_addr, int ram_addr, int byte_count) {
+               int flash_word_addr = flash_addr >> 1;
+               int flash_word_count = ((byte_count + 1) >> 1);
+
+               byte[] flash_page = new byte[flash_page_proto.length];
+               for (int i = 0; i < flash_page.length; i++)
+                       flash_page[i] = flash_page_proto[i];
+
+               flash_page[FLASH_ADDR_HIGH]  = (byte) (flash_word_addr >> 8);
+               flash_page[FLASH_ADDR_LOW]   = (byte) (flash_word_addr);
+               flash_page[RAM_ADDR_HIGH]    = (byte) (ram_addr >> 8);
+               flash_page[RAM_ADDR_LOW]     = (byte) (ram_addr);
+
+               byte flash_words_low = (byte) (flash_word_count);
+               byte flash_words_high = (byte) (flash_word_count >> 8);
+               /* the flashing code has a minor 'bug' */
+               if (flash_words_low != 0)
+                       flash_words_high++;
+
+               flash_page[FLASH_WORDS_HIGH] = (byte) flash_words_high;
+               flash_page[FLASH_WORDS_LOW]  = (byte) flash_words_low;
+               return flash_page;
+       }
+
+       static byte[] set_clkcon_fast = {
+               MOV_direct_data, CLKCON, 0x00
+       };
+
+       static byte[] get_sleep = {
+               MOV_A_direct, SLEEP
+       };
+
+       public void clock_init() throws IOException, InterruptedException {
+               if (debug != null) {
+                       debug.debug_instr(set_clkcon_fast);
+
+                       byte    status;
+                       for (int times = 0; times < 20; times++) {
+                               Thread.sleep(1);
+                               status = debug.debug_instr(get_sleep);
+                               if ((status & SLEEP_XOSC_STB) != 0)
+                                       return;
+                       }
+                       throw new IOException("Failed to initialize target clock");
+               }
+       }
+
+       void action(String in_s, int in_percent) {
+               final String s = in_s;
+               final int percent = in_percent;
+               if (listener != null && !aborted) {
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               try {
+                                                       listener.actionPerformed(new ActionEvent(this,
+                                                                                                percent,
+                                                                                                s));
+                                               } catch (Exception ex) {
+                                               }
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+       }
+
+       void action(int part, int total) {
+               int percent = 100 * part / total;
+               action(String.format("%d/%d (%d%%)",
+                                    part, total, percent),
+                      percent);
+       }
+
+       void altos_run(int pc) throws IOException, InterruptedException {
+               debug.set_pc(pc);
+               int set_pc = debug.get_pc();
+               if (pc != set_pc)
+                       throw new IOException("Failed to set target program counter");
+               debug.resume();
+
+               for (int times = 0; times < 20; times++) {
+                       byte status = debug.read_status();
+                       if ((status & AltosDebug.STATUS_CPU_HALTED) != 0)
+                               return;
+               }
+
+               throw new IOException("Failed to execute program on target");
+       }
+
+       public void flash() {
+               try {
+                       if (!check_rom_config())
+                               throw new IOException("Invalid rom config settings");
+                       if (image.address + image.data.length > 0x8000)
+                               throw new IOException(String.format("Flash image too long %d",
+                                                                   image.address +
+                                                                   image.data.length));
+                       if ((image.address & 0x3ff) != 0)
+                               throw new IOException(String.format("Flash image must start on page boundary (is 0x%x)",
+                                                                   image.address));
+                       int ram_address = 0xf000;
+                       int flash_prog = 0xf400;
+
+                       /*
+                        * Store desired config values into image
+                        */
+                       rom_config.write(image);
+                       /*
+                        * Bring up the clock
+                        */
+                       clock_init();
+
+                       int remain = image.data.length;
+                       int flash_addr = image.address;
+                       int image_start = 0;
+
+                       action("start", 0);
+                       action(0, image.data.length);
+                       while (remain > 0 && !aborted) {
+                               int this_time = remain;
+                               if (this_time > 0x400)
+                                       this_time = 0x400;
+
+                               if (debug != null) {
+                                       /* write the data */
+                                       debug.write_memory(ram_address, image.data,
+                                                          image_start, this_time);
+
+                                       /* write the flash program */
+                                       byte[] flash_page = make_flash_page(flash_addr,
+                                                                           ram_address,
+                                                                           this_time);
+                                       debug.write_memory(flash_prog, flash_page);
+
+                                       altos_run(flash_prog);
+                                       byte[] check = debug.read_memory(flash_addr, this_time);
+                                       for (int i = 0; i < this_time; i++)
+                                               if (check[i] != image.data[image_start + i])
+                                                       throw new IOException(String.format("Flash write failed at 0x%x (%02x != %02x)",
+                                                                                           image.address + image_start + i,
+                                                                                           check[i], image.data[image_start + i]));
+                               } else {
+                                       Thread.sleep(100);
+                               }
+
+                               remain -= this_time;
+                               flash_addr += this_time;
+                               image_start += this_time;
+
+                               action(image.data.length - remain, image.data.length);
+                       }
+                       if (!aborted) {
+                               action("done", 100);
+                               if (debug != null) {
+                                       debug.set_pc(image.address);
+                                       debug.resume();
+                               }
+                       }
+                       if (debug != null)
+                               debug.close();
+               } catch (IOException ie) {
+                       action(ie.getMessage(), -1);
+                       abort();
+               } catch (InterruptedException ie) {
+                       abort();
+               }
+       }
+
+       public void close() {
+               if (debug != null)
+                       debug.close();
+       }
+
+       synchronized public void abort() {
+               aborted = true;
+               close();
+       }
+
+       public void addActionListener(ActionListener l) {
+               listener = l;
+       }
+
+       public boolean check_rom_config() {
+               if (debug == null)
+                       return true;
+               if (rom_config == null)
+                       rom_config = debug.romconfig();
+               return rom_config != null && rom_config.valid();
+       }
+
+       public void set_romconfig (AltosRomconfig romconfig) {
+               rom_config = romconfig;
+       }
+
+       public AltosRomconfig romconfig() {
+               if (!check_rom_config())
+                       return null;
+               return rom_config;
+       }
+
+       public AltosFlash(File in_file, AltosDevice in_debug_dongle)
+               throws IOException, FileNotFoundException, AltosSerialInUseException, InterruptedException {
+               file = in_file;
+               debug_dongle = in_debug_dongle;
+               if (debug_dongle != null)
+                       debug = new AltosDebug(in_debug_dongle);
+               input = new FileInputStream(file);
+               image = new AltosHexfile(input);
+               if (debug != null && !debug.check_connection()) {
+                       debug.close();
+                       throw new IOException("Debug port not connected");
+               }
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosFlashUI.java b/altosui/AltosFlashUI.java
new file mode 100644 (file)
index 0000000..66991d1
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosFlashUI
+       extends AltosDialog
+       implements ActionListener
+{
+       Container       pane;
+       Box             box;
+       JLabel          serial_label;
+       JLabel          serial_value;
+       JLabel          file_label;
+       JLabel          file_value;
+       JProgressBar    pbar;
+       JButton         cancel;
+
+       JFrame          frame;
+
+       // Hex file with rom image
+       File            file;
+
+       // Debug connection
+       AltosDevice     debug_dongle;
+
+       // Desired Rom configuration
+       AltosRomconfig  rom_config;
+
+       // Flash controller
+       AltosFlash      flash;
+
+       public void actionPerformed(ActionEvent e) {
+               if (e.getSource() == cancel) {
+                       if (flash != null)
+                               flash.abort();
+                       setVisible(false);
+                       dispose();
+               } else {
+                       String  cmd = e.getActionCommand();
+                       if (e.getID() == -1) {
+                               JOptionPane.showMessageDialog(frame,
+                                                             e.getActionCommand(),
+                                                             file.toString(),
+                                                             JOptionPane.ERROR_MESSAGE);
+                               setVisible(false);
+                               dispose();
+                       } else if (cmd.equals("done")) {
+                               setVisible(false);
+                               dispose();
+                       } else if (cmd.equals("start")) {
+                               setVisible(true);
+                       } else {
+                               pbar.setValue(e.getID());
+                               pbar.setString(cmd);
+                       }
+               }
+       }
+
+       public void build_dialog() {
+               GridBagConstraints c;
+               Insets il = new Insets(4,4,4,4);
+               Insets ir = new Insets(4,4,4,4);
+
+               pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 0;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               serial_label = new JLabel("Serial:");
+               pane.add(serial_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 1; c.gridy = 0;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               serial_value = new JLabel("");
+               pane.add(serial_value, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.NONE;
+               c.gridx = 0; c.gridy = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               file_label = new JLabel("File:");
+               pane.add(file_label, c);
+
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.gridx = 1; c.gridy = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               file_value = new JLabel(file.toString());
+               pane.add(file_value, c);
+
+               pbar = new JProgressBar();
+               pbar.setMinimum(0);
+               pbar.setMaximum(100);
+               pbar.setValue(0);
+               pbar.setString("");
+               pbar.setStringPainted(true);
+               pbar.setPreferredSize(new Dimension(600, 20));
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0; c.gridy = 2;
+               c.gridwidth = GridBagConstraints.REMAINDER;
+               Insets ib = new Insets(4,4,4,4);
+               c.insets = ib;
+               pane.add(pbar, c);
+
+               cancel = new JButton("Cancel");
+               c = new GridBagConstraints();
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.gridx = 0; c.gridy = 3;
+               c.gridwidth = GridBagConstraints.REMAINDER;
+               Insets ic = new Insets(4,4,4,4);
+               c.insets = ic;
+               pane.add(cancel, c);
+               cancel.addActionListener(this);
+               pack();
+               setLocationRelativeTo(frame);
+       }
+
+       void set_serial(int serial_number) {
+               serial_value.setText(String.format("%d", serial_number));
+       }
+
+       boolean select_source_file() {
+               JFileChooser    hexfile_chooser = new JFileChooser();
+
+               File firmwaredir = AltosUIPreferences.firmwaredir();
+               if (firmwaredir != null)
+                       hexfile_chooser.setCurrentDirectory(firmwaredir);
+
+               hexfile_chooser.setDialogTitle("Select Flash Image");
+               hexfile_chooser.setFileFilter(new FileNameExtensionFilter("Flash Image", "ihx"));
+               int returnVal = hexfile_chooser.showOpenDialog(frame);
+
+               if (returnVal != JFileChooser.APPROVE_OPTION)
+                       return false;
+               file = hexfile_chooser.getSelectedFile();
+               if (file == null)
+                       return false;
+               AltosUIPreferences.set_firmwaredir(file.getParentFile());
+               return true;
+       }
+
+       boolean select_debug_dongle() {
+               debug_dongle = AltosDeviceDialog.show(frame, Altos.product_any);
+
+               if (debug_dongle == null)
+                       return false;
+               return true;
+       }
+
+       boolean update_rom_config_info(AltosRomconfig existing_config) {
+               AltosRomconfig  new_config;
+               new_config = AltosRomconfigUI.show(frame, existing_config);
+               if (new_config == null)
+                       return false;
+               rom_config = new_config;
+               set_serial(rom_config.serial_number);
+               setVisible(true);
+               return true;
+       }
+
+       void exception (Exception e) {
+               if (e instanceof FileNotFoundException) {
+                       JOptionPane.showMessageDialog(frame,
+                                                     ((FileNotFoundException) e).getMessage(),
+                                                     "Cannot open file",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof AltosSerialInUseException) {
+                       JOptionPane.showMessageDialog(frame,
+                                                     String.format("Device \"%s\" already in use",
+                                                                   debug_dongle.toShortString()),
+                                                     "Device in use",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof IOException) {
+                       JOptionPane.showMessageDialog(frame,
+                                                     e.getMessage(),
+                                                     file.toString(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               }
+       }
+
+       class flash_task implements Runnable {
+               AltosFlashUI    ui;
+               Thread          t;
+               AltosFlash      flash;
+
+               public void run () {
+                       try {
+                               flash = new AltosFlash(ui.file, ui.debug_dongle);
+                               flash.addActionListener(ui);
+
+                               final AltosRomconfig    current_config = flash.romconfig();
+
+                               final Semaphore await_rom_config = new Semaphore(0);
+                               SwingUtilities.invokeLater(new Runnable() {
+                                               public void run() {
+                                                       ui.flash = flash;
+                                                       ui.update_rom_config_info(current_config);
+                                                       await_rom_config.release();
+                                               }
+                                       });
+                               await_rom_config.acquire();
+
+                               if (ui.rom_config != null) {
+                                       flash.set_romconfig(ui.rom_config);
+                                       flash.flash();
+                               }
+                       } catch (InterruptedException ee) {
+                               final Exception e = ee;
+                               SwingUtilities.invokeLater(new Runnable() {
+                                               public void run() {
+                                                       ui.exception(e);
+                                               }
+                                       });
+                       } catch (IOException ee) {
+                               final Exception e = ee;
+                               SwingUtilities.invokeLater(new Runnable() {
+                                               public void run() {
+                                                       ui.exception(e);
+                                               }
+                                       });
+                       } catch (AltosSerialInUseException ee) {
+                               final Exception e = ee;
+                               SwingUtilities.invokeLater(new Runnable() {
+                                               public void run() {
+                                                       ui.exception(e);
+                                               }
+                                       });
+                       } finally {
+                               if (flash != null)
+                                       flash.close();
+                       }
+               }
+
+               public flash_task(AltosFlashUI in_ui) {
+                       ui = in_ui;
+                       t = new Thread(this);
+                       t.start();
+               }
+       }
+
+       flash_task      flasher;
+
+       /*
+        * Execute the steps for flashing
+        * a device. Note that this returns immediately;
+        * this dialog is not modal
+        */
+       void showDialog() {
+               if (!select_debug_dongle())
+                       return;
+               if (!select_source_file())
+                       return;
+               build_dialog();
+               flash_task      f = new flash_task(this);
+       }
+
+       static void show(JFrame frame) {
+               AltosFlashUI    ui = new AltosFlashUI(frame);
+
+               ui.showDialog();
+       }
+
+       public AltosFlashUI(JFrame in_frame) {
+               super(in_frame, "Program Altusmetrum Device", false);
+
+               frame = in_frame;
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosFlightDisplay.java b/altosui/AltosFlightDisplay.java
new file mode 100644 (file)
index 0000000..826f952
--- /dev/null
@@ -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 org.altusmetrum.AltosLib.*;
+
+public interface AltosFlightDisplay {
+       void reset();
+
+       void show(AltosState state, int crc_errors);
+
+       void set_font();
+}
diff --git a/altosui/AltosFlightInfoTableModel.java b/altosui/AltosFlightInfoTableModel.java
new file mode 100644 (file)
index 0000000..77969a8
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosFlightInfoTableModel extends AbstractTableModel {
+       final static private String[] columnNames = {"Field", "Value"};
+
+       int     rows;
+       int     cols;
+       private String[][] data;
+
+       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 || col >= cols)
+                       return "";
+               return data[row][col];
+       }
+
+       int[]   current_row;
+
+       public void reset() {
+               for (int i = 0; i < cols / 2; i++)
+                       current_row[i] = 0;
+       }
+
+       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() {
+               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/AltosFlightStats.java b/altosui/AltosFlightStats.java
new file mode 100644 (file)
index 0000000..ab094c8
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosFlightStats {
+       double          max_height;
+       double          max_speed;
+       double          max_acceleration;
+       double[]        state_speed = new double[Altos.ao_flight_invalid + 1];
+       double[]        state_baro_speed = new double[Altos.ao_flight_invalid + 1];
+       double[]        state_accel = new double[Altos.ao_flight_invalid + 1];
+       int[]           state_count = new int[Altos.ao_flight_invalid + 1];
+       double[]        state_start = new double[Altos.ao_flight_invalid + 1];
+       double[]        state_end = new double[Altos.ao_flight_invalid + 1];
+       int             serial;
+       int             flight;
+       int             year, month, day;
+       int             hour, minute, second;
+
+       double landed_time(AltosRecordIterable iterable) {
+               AltosState      state = null;
+               for (AltosRecord record : iterable) {
+                       state = new AltosState(record, state);
+
+                       if (state.state == Altos.ao_flight_landed)
+                               break;
+               }
+
+               double  landed_height = state.height;
+
+               state = null;
+
+               boolean above = true;
+
+               double  landed_time = -1000;
+
+               for (AltosRecord record : iterable) {
+                       state = new AltosState(record, state);
+
+                       if (state.height > landed_height + 10) {
+                               above = true;
+                       } else {
+                               if (above && state.height < landed_height + 2) {
+                                       above = false;
+                                       landed_time = state.time;
+                               }
+                       }
+               }
+               if (landed_time == -1000)
+                       landed_time = state.time;
+               return landed_time;
+       }
+
+       double boost_time(AltosRecordIterable iterable) {
+               double boost_time = -1000;
+
+               AltosState state = null;
+
+               for (AltosRecord record : iterable) {
+                       state = new AltosState(record, state);
+                       
+                       if (state.acceleration < 1)
+                               boost_time = state.time;
+                       if (state.state >= Altos.ao_flight_boost)
+                               break;
+               }
+               if (boost_time == -1000)
+                       boost_time = state.time;
+               return boost_time;
+       }
+
+
+       public AltosFlightStats(AltosRecordIterable iterable) throws InterruptedException, IOException {
+               AltosState      state = null;
+               AltosState      new_state = null;
+               double          boost_time = boost_time(iterable);
+               double          end_time = 0;
+               double          landed_time = landed_time(iterable);
+
+               year = month = day = -1;
+               hour = minute = second = -1;
+               serial = flight = -1;
+               for (AltosRecord record : iterable) {
+                       if (serial < 0)
+                               serial = record.serial;
+                       if ((record.seen & AltosRecord.seen_flight) != 0 && flight < 0)
+                               flight = record.flight;
+                       new_state = new AltosState(record, state);
+                       end_time = new_state.time;
+                       state = new_state;
+                       if (state.time >= boost_time && state.state < Altos.ao_flight_boost)
+                               state.state = Altos.ao_flight_boost;
+                       if (state.time >= landed_time && state.state < Altos.ao_flight_landed)
+                               state.state = Altos.ao_flight_landed;
+                       if (0 <= state.state && state.state < Altos.ao_flight_invalid) {
+                               if (state.state >= Altos.ao_flight_boost) {
+                                       if (state.gps != null && state.gps.locked &&
+                                           year < 0) {
+                                               year = state.gps.year;
+                                               month = state.gps.month;
+                                               day = state.gps.day;
+                                               hour = state.gps.hour;
+                                               minute = state.gps.minute;
+                                               second = state.gps.second;
+                                       }
+                               }
+                               state_accel[state.state] += state.acceleration;
+                               state_speed[state.state] += state.speed;
+                               state_baro_speed[state.state] += state.baro_speed;
+                               state_count[state.state]++;
+                               if (state_start[state.state] == 0.0)
+                                       state_start[state.state] = state.time;
+                               if (state_end[state.state] < state.time)
+                                       state_end[state.state] = state.time;
+                               max_height = state.max_height;
+                               if (state.max_speed != 0)
+                                       max_speed = state.max_speed;
+                               else
+                                       max_speed = state.max_baro_speed;
+                               max_acceleration = state.max_acceleration;
+                       }
+               }
+               for (int s = Altos.ao_flight_startup; s <= Altos.ao_flight_landed; s++) {
+                       if (state_count[s] > 0) {
+                               state_speed[s] /= state_count[s];
+                               state_baro_speed[s] /= state_count[s];
+                               state_accel[s] /= state_count[s];
+                       }
+                       if (state_start[s] == 0)
+                               state_start[s] = end_time;
+                       if (state_end[s] == 0)
+                               state_end[s] = end_time;
+               }
+       }
+}
diff --git a/altosui/AltosFlightStatsTable.java b/altosui/AltosFlightStatsTable.java
new file mode 100644 (file)
index 0000000..87ba6aa
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosFlightStatsTable extends JComponent {
+       GridBagLayout   layout;
+
+       class FlightStat {
+               JLabel          label;
+               JTextField      value;
+
+               public FlightStat(GridBagLayout layout, int y, String label_text, String ... values) {
+                       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(label_text);
+                       label.setFont(Altos.label_font);
+                       label.setHorizontalAlignment(SwingConstants.LEFT);
+                       c.gridx = 0; c.gridy = y;
+                       c.anchor = GridBagConstraints.WEST;
+                       c.fill = GridBagConstraints.VERTICAL;
+                       c.weightx = 0;
+                       layout.setConstraints(label, c);
+                       add(label);
+
+                       for (int j = 0; j < values.length; j++) {
+                               value = new JTextField(values[j]);
+                               value.setFont(Altos.value_font);
+                               value.setHorizontalAlignment(SwingConstants.RIGHT);
+                               c.gridx = j+1; c.gridy = y;
+                               c.anchor = GridBagConstraints.EAST;
+                               c.fill = GridBagConstraints.BOTH;
+                               c.weightx = 1;
+                               layout.setConstraints(value, c);
+                               add(value);
+                       }
+               }
+
+       }
+
+       public AltosFlightStatsTable(AltosFlightStats stats) {
+               layout = new GridBagLayout();
+
+               setLayout(layout);
+               int y = 0;
+               new FlightStat(layout, y++, "Serial", String.format("%d", stats.serial));
+               new FlightStat(layout, y++, "Flight", String.format("%d", stats.flight));
+               if (stats.year > 0)
+                       new FlightStat(layout, y++, "Date",
+                                      String.format("%04d-%02d-%02d", stats.year, stats.month, stats.day));
+               if (stats.hour > 0)
+                       new FlightStat(layout, y++, "Time",
+                                      String.format("%02d:%02d:%02d UTC", stats.hour, stats.minute, stats.second));
+               new FlightStat(layout, y++, "Maximum height",
+                              String.format("%5.0f m", stats.max_height),
+                              String.format("%5.0f ft", AltosConvert.meters_to_feet(stats.max_height)));
+               new FlightStat(layout, y++, "Maximum speed",
+                              String.format("%5.0f m/s", stats.max_speed),
+                              String.format("%5.0f mph", AltosConvert.meters_to_mph(stats.max_speed)),
+                              String.format("Mach %4.1f", AltosConvert.meters_to_mach(stats.max_speed)));
+               if (stats.max_acceleration != AltosRecord.MISSING) {
+                       new FlightStat(layout, y++, "Maximum boost acceleration",
+                                      String.format("%5.0f m/s²", stats.max_acceleration),
+                                      String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.max_acceleration)),
+                                      String.format("%5.0f G", AltosConvert.meters_to_g(stats.max_acceleration)));
+                       new FlightStat(layout, y++, "Average boost acceleration",
+                                      String.format("%5.0f m/s²", stats.state_accel[Altos.ao_flight_boost]),
+                                      String.format("%5.0f ft/s²", AltosConvert.meters_to_feet(stats.state_accel[Altos.ao_flight_boost])),
+                                      String.format("%5.0f G", AltosConvert.meters_to_g(stats.state_accel[Altos.ao_flight_boost])));
+               }
+               new FlightStat(layout, y++, "Drogue descent rate",
+                              String.format("%5.0f m/s", stats.state_baro_speed[Altos.ao_flight_drogue]),
+                              String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_drogue])));
+               new FlightStat(layout, y++, "Main descent rate",
+                              String.format("%5.0f m/s", stats.state_baro_speed[Altos.ao_flight_main]),
+                              String.format("%5.0f ft/s", AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_main])));
+               for (int s = Altos.ao_flight_boost; s <= Altos.ao_flight_main; s++) {
+                       new FlightStat(layout, y++, String.format("%s time", AltosLib.state_name_capital(s)),
+                                      String.format("%6.0f s", stats.state_end[s] - stats.state_start[s]));
+               }
+               new FlightStat(layout, y++, "Flight Time",
+                              String.format("%6.0f s", stats.state_end[Altos.ao_flight_main] -
+                                            stats.state_start[Altos.ao_flight_boost]));
+               
+       }
+       
+}
\ No newline at end of file
diff --git a/altosui/AltosFlightStatus.java b/altosui/AltosFlightStatus.java
new file mode 100644 (file)
index 0000000..6a35100
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+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("");
+               }
+
+               void set_font() {
+                       label.setFont(Altos.status_font);
+                       value.setFont(Altos.status_font);
+               }
+
+               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");
+               }
+       }
+
+       RSSI rssi;
+
+       class LastPacket extends FlightValue {
+               void show(AltosState state, int crc_errors) {
+                       long secs = (System.currentTimeMillis() - state.report_time + 500) / 1000;
+                       value.setText(String.format("%d", secs));
+               }
+               public LastPacket(GridBagLayout layout, int x) {
+                       super (layout, x, "Age");
+               }
+       }
+
+       LastPacket last_packet;
+
+       public void reset () {
+               call.reset();
+               serial.reset();
+               flight.reset();
+               flight_state.reset();
+               rssi.reset();
+               last_packet.reset();
+       }
+
+       public void set_font () {
+               call.set_font();
+               serial.set_font();
+               flight.set_font();
+               flight_state.set_font();
+               rssi.set_font();
+               last_packet.set_font();
+       }
+
+       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);
+               last_packet.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);
+               last_packet = new LastPacket(layout, 5);
+       }
+}
diff --git a/altosui/AltosFlightStatusTableModel.java b/altosui/AltosFlightStatusTableModel.java
new file mode 100644 (file)
index 0000000..c2cf8cd
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosFlightStatusTableModel extends AbstractTableModel {
+       private String[] columnNames = {
+               String.format("Height (%s)", AltosConvert.show_distance_units()),
+               "State",
+               "RSSI (dBm)",
+               String.format("Speed (%s)", AltosConvert.show_speed_unit())
+       };
+       private Object[] data = { 0, "idle", 0, 0 };
+
+       public int getColumnCount() { return columnNames.length; }
+       public int getRowCount() { return 2; }
+       public Object getValueAt(int row, int col) {
+               if (row == 0)
+                       return columnNames[col];
+               return data[col];
+       }
+
+       public void setValueAt(Object value, int col) {
+               data[col] = value;
+               fireTableCellUpdated(1, col);
+       }
+
+       public void setValueAt(Object value, int row, int col) {
+               setValueAt(value, col);
+       }
+
+       public void set(AltosState state) {
+               setValueAt(String.format("%1.0f", AltosConvert.distance(state.height), 0);
+               setValueAt(state.data.state(), 1);
+               setValueAt(state.data.rssi, 2);
+               double speed = state.baro_speed;
+               if (state.ascent)
+                       speed = state.speed;
+               setValueAt(String.format("%1.0f", AltosConvert.speed(speed)), 3);
+       }
+}
diff --git a/altosui/AltosFlightStatusUpdate.java b/altosui/AltosFlightStatusUpdate.java
new file mode 100644 (file)
index 0000000..d70fc7f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2012 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.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosFlightStatusUpdate implements ActionListener {
+
+       public AltosState       saved_state;
+       AltosFlightStatus       flightStatus;
+
+       public void actionPerformed (ActionEvent e) {
+               if (saved_state != null)
+                       flightStatus.show(saved_state, 0);
+       }
+
+       public AltosFlightStatusUpdate (AltosFlightStatus in_flightStatus) {
+               flightStatus = in_flightStatus;
+       }
+}
+
diff --git a/altosui/AltosFlightUI.java b/altosui/AltosFlightUI.java
new file mode 100644 (file)
index 0000000..ddc54cb
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosFlightUI extends AltosFrame implements AltosFlightDisplay, AltosFontListener {
+       AltosVoice              voice;
+       AltosFlightReader       reader;
+       AltosDisplayThread      thread;
+
+       JTabbedPane     pane;
+
+       AltosPad        pad;
+       AltosAscent     ascent;
+       AltosDescent    descent;
+       AltosLanded     landed;
+       AltosCompanionInfo      companion;
+       AltosSiteMap    sitemap;
+       boolean         has_map;
+       boolean         has_companion;
+
+       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 set_font() {
+               pad.set_font();
+               ascent.set_font();
+               descent.set_font();
+               landed.set_font();
+               flightStatus.set_font();
+               flightInfo.set_font();
+               sitemap.set_font();
+               companion.set_font();
+       }
+
+       public void font_size_changed(int font_size) {
+               set_font();
+       }
+
+
+       AltosFlightStatusUpdate status_update;
+
+       public void show(AltosState state, int crc_errors) {
+               status_update.saved_state = state;
+               JComponent tab = which_tab(state);
+               try {
+               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);
+
+               if (state.data.companion != null) {
+                       if (!has_companion) {
+                               pane.add("Companion", companion);
+                               has_companion= true;
+                       }
+                       companion.show(state, crc_errors);
+               } else {
+                       if (has_companion) {
+                               pane.remove(companion);
+                               has_companion = false;
+                       }
+               }
+               if (state.gps != null && state.gps.connected) {
+                       if (!has_map) {
+                               pane.add("Site Map", sitemap);
+                               has_map = true;
+                       }
+                       sitemap.show(state, crc_errors);
+               } else {
+                       if (has_map) {
+                               pane.remove(sitemap);
+                               has_map = false;
+                       }
+               }
+               } catch (Exception e) {
+                       System.out.print("Show exception" + e);
+               }
+       }
+
+       public void set_exit_on_close() {
+               exit_on_close = true;
+       }
+
+       Container       bag;
+       AltosFreqList   frequencies;
+       JComboBox       telemetries;
+       JLabel          telemetry;
+
+       ActionListener  show_timer;
+
+       public AltosFlightUI(AltosVoice in_voice, AltosFlightReader in_reader, final int serial) {
+               AltosUIPreferences.set_component(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
+                       frequencies = new AltosFreqList(AltosUIPreferences.frequency(serial));
+                       frequencies.set_product("Monitor");
+                       frequencies.set_serial(serial);
+                       frequencies.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               double frequency = frequencies.frequency();
+                                               try {
+                                                       reader.set_frequency(frequency);
+                                               } catch (TimeoutException te) {
+                                               } catch (InterruptedException ie) {
+                                               }
+                                               reader.save_frequency();
+                                       }
+                       });
+                       c.gridx = 0;
+                       c.gridy = 0;
+                       c.weightx = 0;
+                       c.weighty = 0;
+                       c.insets = new Insets(3, 3, 3, 3);
+                       c.fill = GridBagConstraints.NONE;
+                       c.anchor = GridBagConstraints.WEST;
+                       bag.add (frequencies, c);
+
+                       // Telemetry format menu
+                       if (reader.supports_telemetry(Altos.ao_telemetry_standard)) {
+                               telemetries = new JComboBox();
+                               for (int i = 1; i <= Altos.ao_telemetry_max; i++) 
+                                       telemetries.addItem(Altos.telemetry_name(i));
+                               int telemetry = AltosPreferences.telemetry(serial);
+                               if (telemetry <= Altos.ao_telemetry_off ||
+                                   telemetry > Altos.ao_telemetry_max)
+                                       telemetry = Altos.ao_telemetry_standard;
+                               telemetries.setSelectedIndex(telemetry - 1);
+                               telemetries.setMaximumRowCount(Altos.ao_telemetry_max);
+                               telemetries.setPreferredSize(null);
+                               telemetries.revalidate();
+                               telemetries.addActionListener(new ActionListener() {
+                                               public void actionPerformed(ActionEvent e) {
+                                                       int telemetry = telemetries.getSelectedIndex() + 1;
+                                                       reader.set_telemetry(telemetry);
+                                                       reader.save_telemetry();
+                                               }
+                                       });
+                               c.gridx = 1;
+                               c.gridy = 0;
+                               c.weightx = 0;
+                               c.weighty = 0;
+                               c.fill = GridBagConstraints.NONE;
+                               c.anchor = GridBagConstraints.WEST;
+                               bag.add (telemetries, c);
+                               c.insets = new Insets(0, 0, 0, 0);
+                       } else {
+                               String  version;
+
+                               if (reader.supports_telemetry(Altos.ao_telemetry_0_9))
+                                       version = "Telemetry: 0.9";
+                               else if (reader.supports_telemetry(Altos.ao_telemetry_0_8))
+                                       version = "Telemetry: 0.8";
+                               else
+                                       version = "Telemetry: None";
+
+                               telemetry = new JLabel(version);
+                               c.gridx = 1;
+                               c.gridy = 0;
+                               c.weightx = 0;
+                               c.weighty = 0;
+                               c.fill = GridBagConstraints.NONE;
+                               c.anchor = GridBagConstraints.WEST;
+                               bag.add (telemetry, c);
+                               c.insets = new Insets(0, 0, 0, 0);
+                       }
+               }
+
+               /* Flight status is always visible */
+               flightStatus = new AltosFlightStatus();
+               c.gridx = 0;
+               c.gridy = 1;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.gridwidth = 2;
+               bag.add(flightStatus, c);
+               c.gridwidth = 1;
+
+               /* 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(reader);
+               pane.add("Landed", landed);
+
+               flightInfo = new AltosInfoTable();
+               pane.add("Table", new JScrollPane(flightInfo));
+
+               companion = new AltosCompanionInfo();
+               has_companion = false;
+
+               sitemap = new AltosSiteMap();
+               has_map = false;
+
+               /* 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;
+               c.gridwidth = 2;
+               bag.add(pane, c);
+
+               setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+
+               AltosUIPreferences.register_font_listener(this);
+
+               addWindowListener(new WindowAdapter() {
+                               @Override
+                               public void windowClosing(WindowEvent e) {
+                                       disconnect();
+                                       setVisible(false);
+                                       dispose();
+                                       AltosUIPreferences.unregister_font_listener(AltosFlightUI.this);
+                                       if (exit_on_close)
+                                               System.exit(0);
+                               }
+                       });
+
+               pack();
+               setVisible(true);
+
+               thread = new AltosDisplayThread(this, voice, this, reader);
+
+               status_update = new AltosFlightStatusUpdate(flightStatus);
+
+               new javax.swing.Timer(100, status_update).start();
+
+               thread.start();
+       }
+
+       public AltosFlightUI (AltosVoice in_voice, AltosFlightReader in_reader) {
+               this(in_voice, in_reader, -1);
+       }
+}
diff --git a/altosui/AltosFontListener.java b/altosui/AltosFontListener.java
new file mode 100644 (file)
index 0000000..0dda0f2
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2011 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 AltosFontListener {
+       void font_size_changed(int font_size);
+}
diff --git a/altosui/AltosFrame.java b/altosui/AltosFrame.java
new file mode 100644 (file)
index 0000000..7059863
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+class AltosFrameListener extends WindowAdapter {
+       public void windowClosing (WindowEvent e) {
+               AltosUIPreferences.unregister_ui_listener((AltosFrame) e.getWindow());
+       }
+}
+
+public class AltosFrame extends JFrame implements AltosUIListener {
+
+       public void ui_changed(String look_and_feel) {
+               SwingUtilities.updateComponentTreeUI(this);
+               this.pack();
+       }
+
+       public AltosFrame() {
+               AltosUIPreferences.register_ui_listener(this);
+               addWindowListener(new AltosFrameListener());
+       }
+
+       public AltosFrame(String name) {
+               super(name);
+               AltosUIPreferences.register_ui_listener(this);
+               addWindowListener(new AltosFrameListener());
+       }
+}
diff --git a/altosui/AltosFreqList.java b/altosui/AltosFreqList.java
new file mode 100644 (file)
index 0000000..1bbc97c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2011 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 org.altusmetrum.AltosLib.*;
+
+public class AltosFreqList extends JComboBox {
+
+       String  product;
+       int     serial;
+       int     calibrate;
+
+       public void set_frequency(double new_frequency) {
+               int i;
+               for (i = 0; i < getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) getItemAt(i);
+                       
+                       if (f.close(new_frequency)) {
+                               setSelectedIndex(i);
+                               return;
+                       }
+               }
+               for (i = 0; i < getItemCount(); i++) {
+                       AltosFrequency  f = (AltosFrequency) getItemAt(i);
+                       
+                       if (new_frequency < f.frequency)
+                               break;
+               }
+               String  description = String.format("%s serial %d", product, serial);
+               AltosFrequency  frequency = new AltosFrequency(new_frequency, description);
+               AltosUIPreferences.add_common_frequency(frequency);
+               insertItemAt(frequency, i);
+               setMaximumRowCount(getItemCount());
+       }
+
+       public void set_product(String new_product) {
+               product = new_product;
+       }
+               
+       public void set_serial(int new_serial) {
+               serial = new_serial;
+       }
+
+       public double frequency() {
+               AltosFrequency  f = (AltosFrequency) getSelectedItem();
+               if (f != null)
+                       return f.frequency;
+               return 434.550;
+       }
+
+       public AltosFreqList () {
+               super(AltosUIPreferences.common_frequencies());
+               setMaximumRowCount(getItemCount());
+               setEditable(false);
+               product = "Unknown";
+               serial = 0;
+       }
+
+       public AltosFreqList(double in_frequency) {
+               this();
+               set_frequency(in_frequency);
+       }
+}
diff --git a/altosui/AltosGraph.java b/altosui/AltosGraph.java
new file mode 100644 (file)
index 0000000..54d2bb0
--- /dev/null
@@ -0,0 +1,27 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.io.*;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.ChartUtilities;
+import org.altusmetrum.AltosLib.*;
+
+abstract class AltosGraph {
+    public String filename;
+    public abstract void addData(AltosDataPoint d);
+    public abstract JFreeChart createChart();
+    public String title;
+    public void toPNG() throws java.io.IOException { toPNG(300, 500); }
+    public void toPNG(int width, int height)
+        throws java.io.IOException
+    {
+        File pngout = new File(filename);
+        JFreeChart chart = createChart();
+        ChartUtilities.saveChartAsPNG(pngout, chart, width, height);
+        System.out.println("Created " + filename);
+    }
+}
diff --git a/altosui/AltosGraphTime.java b/altosui/AltosGraphTime.java
new file mode 100644 (file)
index 0000000..0955f6e
--- /dev/null
@@ -0,0 +1,236 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.lang.*;
+import java.io.*;
+import java.util.concurrent.*;
+import java.util.*;
+import java.text.*;
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.HashMap;
+import org.altusmetrum.AltosLib.*;
+
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.AxisLocation;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.labels.StandardXYToolTipGenerator;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.plot.ValueMarker;
+import org.jfree.chart.renderer.xy.StandardXYItemRenderer;
+import org.jfree.chart.renderer.xy.XYItemRenderer;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.ui.RectangleAnchor;
+import org.jfree.ui.TextAnchor;
+
+class AltosGraphTime extends AltosGraph {
+    static interface Element {
+        void attachGraph(AltosGraphTime g);
+        void gotTimeData(double time, AltosDataPoint d);
+        void addToPlot(AltosGraphTime g, XYPlot plot);
+    }
+
+    static class TimeAxis implements Element {
+        private int axis;
+        private Color color;
+        private String label;
+        private AxisLocation locn;
+        private double min_y = Double.NaN;
+
+        public TimeAxis(int axis, String label, Color color, AxisLocation locn)
+        {
+            this.axis = axis;
+            this.color = color;
+            this.label = label;
+            this.locn = locn;
+        }
+
+        public void setLowerBound(double min_y) {
+            this.min_y = min_y;
+        }
+
+        public void attachGraph(AltosGraphTime g) { return; }
+        public void gotTimeData(double time, AltosDataPoint d) { return; }
+
+        public void addToPlot(AltosGraphTime g, XYPlot plot) {
+            NumberAxis numAxis = new NumberAxis(label);
+            if (!Double.isNaN(min_y))
+                numAxis.setLowerBound(min_y);
+            plot.setRangeAxis(axis, numAxis);
+            plot.setRangeAxisLocation(axis, locn);
+            numAxis.setLabelPaint(color);
+            numAxis.setTickLabelPaint(color);
+            numAxis.setAutoRangeIncludesZero(false);
+        }
+    }
+
+    abstract static class TimeSeries implements Element {
+        protected XYSeries series;
+        private String axisName;
+        private Color color;
+
+        public TimeSeries(String axisName, String label, Color color) {
+            this.series = new XYSeries(label);
+            this.axisName = axisName;
+            this.color = color;
+        }
+
+        public void attachGraph(AltosGraphTime g) {
+            g.setAxis(this, axisName, color);
+        }
+        abstract public void gotTimeData(double time, AltosDataPoint d);
+
+        public void addToPlot(AltosGraphTime g, XYPlot plot) {
+            XYSeriesCollection dataset = new XYSeriesCollection();
+            dataset.addSeries(this.series);
+
+            XYItemRenderer renderer = new StandardXYItemRenderer();
+            renderer.setSeriesPaint(0, color);
+
+            int dataNum = g.getDataNum(this);
+            int axisNum = g.getAxisNum(this);
+
+            plot.setDataset(dataNum, dataset);
+            plot.mapDatasetToRangeAxis(dataNum, axisNum);
+            plot.setRenderer(dataNum, renderer);
+        }
+    }
+
+    static class StateMarker implements Element {
+       private LinkedList<Double> times = new LinkedList<Double>();
+        private String name;
+        private int state;
+       private int prev_state = Altos.ao_flight_startup;
+
+        StateMarker(int state, String name) {
+            this.state = state;
+            this.name = name;
+        }
+
+        public void attachGraph(AltosGraphTime g) { return; }
+        public void gotTimeData(double time, AltosDataPoint d) {
+           if (prev_state != state && d.state() == state)
+               times.add(time);
+           prev_state = d.state();
+        }
+
+        public void addToPlot(AltosGraphTime g, XYPlot plot) {
+           for (double time : times) {
+               ValueMarker m = new ValueMarker(time);
+               m.setLabel(name);
+               m.setLabelAnchor(RectangleAnchor.TOP_RIGHT);
+               m.setLabelTextAnchor(TextAnchor.TOP_LEFT);
+               plot.addDomainMarker(m);
+           }
+        }
+    }
+
+    private String callsign = null;
+    private Integer serial = null;
+    private Integer flight = null; 
+
+    private ArrayList<Element> elements;
+    private HashMap<String,Integer> axes;
+    private HashMap<Element,Integer> datasets;
+    private ArrayList<Integer> datasetAxis;
+
+    public AltosGraphTime(String title) {
+        this.filename = title.toLowerCase().replaceAll("[^a-z0-9]","_")+".png";
+        this.title = title;
+        this.elements = new ArrayList<Element>();
+        this.axes = new HashMap<String,Integer>();
+        this.datasets = new HashMap<Element,Integer>();
+        this.datasetAxis = new ArrayList<Integer>();
+    }
+
+    public AltosGraphTime addElement(Element e) {
+        e.attachGraph(this);
+        elements.add(e);
+        return this;
+    }
+
+    public void setAxis(Element ds, String axisName, Color color) {
+        Integer axisNum = axes.get(axisName);
+        int dsNum = datasetAxis.size();
+        if (axisNum == null) {
+            axisNum = newAxis(axisName, color);
+        }
+        datasets.put(ds, dsNum);
+        datasetAxis.add(axisNum);
+    }
+
+    public int getAxisNum(Element ds) {
+        return datasetAxis.get( datasets.get(ds) ).intValue();
+    }
+    public int getDataNum(Element ds) {
+        return datasets.get(ds).intValue();
+    }
+
+    private Integer newAxis(String name, Color color) {
+        int cnt = axes.size();
+        AxisLocation locn = AxisLocation.BOTTOM_OR_LEFT;
+        if (cnt > 0) {
+            locn = AxisLocation.TOP_OR_RIGHT;
+        }
+        Integer res = new Integer(cnt);
+        axes.put(name, res);
+        this.addElement(new TimeAxis(cnt, name, color, locn));
+        return res;
+    }
+
+    public void addData(AltosDataPoint d) {
+        double time = d.time();
+        for (Element e : elements) {
+            e.gotTimeData(time, d);
+        }
+        if (callsign == null) callsign = d.callsign();
+        if (serial == null) serial = new Integer(d.serial());
+        if (flight == null) flight = new Integer(d.flight());
+    }
+
+    public JFreeChart createChart() {
+        NumberAxis xAxis = new NumberAxis("Time (s)");
+        xAxis.setAutoRangeIncludesZero(false);
+        XYItemRenderer renderer = new XYLineAndShapeRenderer(true, false);
+        XYPlot plot = new XYPlot();
+        plot.setDomainAxis(xAxis);
+        plot.setRenderer(renderer);
+        plot.setOrientation(PlotOrientation.VERTICAL);
+
+        if (serial != null && flight != null) {
+            title = serial + "/" + flight + ": " + title;
+        }
+        if (callsign != null) {
+            title = callsign + " - " + title;
+        }
+
+        renderer.setBaseToolTipGenerator(new StandardXYToolTipGenerator());
+        JFreeChart chart = new JFreeChart(title, JFreeChart.DEFAULT_TITLE_FONT,
+                                plot, true);
+        ChartUtilities.applyCurrentTheme(chart);
+
+        plot.setDomainPannable(true);
+        plot.setRangePannable(true);
+   
+        for (Element e : elements) {
+            e.addToPlot(this, plot);
+        }
+
+        return chart;
+    }
+
+    public void toPNG() throws java.io.IOException {
+        if (axes.size() > 1) {
+            toPNG(800, 500);
+        } else {
+            toPNG(300, 500);
+        }
+    }
+}
diff --git a/altosui/AltosGraphUI.java b/altosui/AltosGraphUI.java
new file mode 100644 (file)
index 0000000..edde130
--- /dev/null
@@ -0,0 +1,313 @@
+
+// Copyright (c) 2010 Anthony Towns
+// GPL v2 or later
+
+package altosui;
+
+import java.io.*;
+import java.util.ArrayList;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.*;
+import org.altusmetrum.AltosLib.*;
+
+import org.jfree.chart.ChartPanel;
+import org.jfree.chart.ChartUtilities;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.AxisLocation;
+import org.jfree.ui.ApplicationFrame;
+import org.jfree.ui.RefineryUtilities;
+
+public class AltosGraphUI extends AltosFrame 
+{
+    JTabbedPane        pane;
+
+    static final private Color red = new Color(194,31,31);
+    static final private Color green = new Color(31,194,31);
+    static final private Color blue = new Color(31,31,194);
+    static final private Color black = new Color(31,31,31);
+    static final private Color yellow = new Color(194,194,31);
+    static final private Color cyan = new Color(31,194,194);
+    static final private Color magenta = new Color(194,31,194);
+
+    static private class OverallGraphs {
+        AltosGraphTime.Element height = 
+               new AltosGraphTime.TimeSeries(String.format("Height (%s)", AltosConvert.height.show_units()), "Height (AGL)", red) {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                       double  height = d.height();
+                       if (height != AltosRecord.MISSING)
+                               series.add(time, AltosConvert.height.value(height));
+                } 
+            };
+    
+        AltosGraphTime.Element speed =
+               new AltosGraphTime.TimeSeries(String.format("Speed (%s)", AltosConvert.speed.show_units()), "Vertical Speed", green) { 
+                public void gotTimeData(double time, AltosDataPoint d) {
+                   double      speed;
+                   if (d.state() < Altos.ao_flight_drogue && d.has_accel()) {
+                       speed = d.accel_speed();
+                    } else {
+                       speed = d.baro_speed();
+                    }
+                   if (speed != AltosRecord.MISSING)
+                           series.add(time, AltosConvert.speed.value(speed));
+                }
+            };
+    
+        AltosGraphTime.Element acceleration =
+               new AltosGraphTime.TimeSeries(String.format("Acceleration (%s)",
+                                                           AltosConvert.accel.show_units()),
+                                             "Axial Acceleration", blue)
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                   double acceleration = d.acceleration();
+                   if (acceleration != AltosRecord.MISSING)
+                           series.add(time, AltosConvert.accel.value(acceleration));
+                }
+            };
+    
+        AltosGraphTime.Element temperature =
+            new AltosGraphTime.TimeSeries("Temperature (\u00B0C)", 
+                    "Board temperature", red) 
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                   double temp = d.temperature();
+                   if (temp != AltosRecord.MISSING)
+                       series.add(time, d.temperature());
+                }
+            };
+    
+        AltosGraphTime.Element drogue_voltage =
+            new AltosGraphTime.TimeSeries("Voltage (V)", "Drogue Continuity", yellow) 
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                   double v = d.drogue_voltage();
+                   if (v != AltosRecord.MISSING)
+                       series.add(time, v);
+                }
+            };
+    
+        AltosGraphTime.Element main_voltage =
+            new AltosGraphTime.TimeSeries("Voltage (V)", "Main Continuity", magenta) 
+            {
+                public void gotTimeData(double time, AltosDataPoint d) {
+                   double v = d.main_voltage();
+                   if (v != AltosRecord.MISSING)
+                       series.add(time, v);
+                }
+            };
+    
+        AltosGraphTime.Element e_pad    = new AltosGraphTime.StateMarker(Altos.ao_flight_pad, "Pad");
+        AltosGraphTime.Element e_boost  = new AltosGraphTime.StateMarker(Altos.ao_flight_boost, "Boost");
+        AltosGraphTime.Element e_fast   = new AltosGraphTime.StateMarker(Altos.ao_flight_fast, "Fast");
+        AltosGraphTime.Element e_coast  = new AltosGraphTime.StateMarker(Altos.ao_flight_coast, "Coast");
+       AltosGraphTime.Element e_drogue = new AltosGraphTime.StateMarker(Altos.ao_flight_drogue, "Drogue");
+       AltosGraphTime.Element e_main   = new AltosGraphTime.StateMarker(Altos.ao_flight_main, "Main");
+        AltosGraphTime.Element e_landed = new AltosGraphTime.StateMarker(Altos.ao_flight_landed, "Landed");
+    
+        protected AltosGraphTime myAltosGraphTime(String suffix) {
+            return (new AltosGraphTime("Overall " + suffix))
+                .addElement(e_boost)
+               .addElement(e_fast)
+               .addElement(e_coast)
+                .addElement(e_drogue)
+                .addElement(e_main)
+                .addElement(e_landed);
+        }
+    
+        public ArrayList<AltosGraph> graphs() {
+            ArrayList<AltosGraph> graphs = new ArrayList<AltosGraph>();
+    
+           graphs.add( myAltosGraphTime("Summary")
+                       .addElement(height)
+                       .addElement(speed)
+                       .addElement(acceleration) );
+
+           graphs.add( myAltosGraphTime("Summary")
+                       .addElement(height)
+                       .addElement(speed));
+    
+            graphs.add( myAltosGraphTime("Altitude")
+                    .addElement(height) );
+    
+            graphs.add( myAltosGraphTime("Speed")
+                    .addElement(speed) );
+    
+           graphs.add( myAltosGraphTime("Acceleration")
+                       .addElement(acceleration) );
+    
+            graphs.add( myAltosGraphTime("Temperature")
+                    .addElement(temperature) );
+    
+           graphs.add( myAltosGraphTime("Continuity")
+                       .addElement(drogue_voltage)
+                       .addElement(main_voltage) );
+    
+            return graphs;
+        }
+    }
+    
+    static private class AscentGraphs extends OverallGraphs {
+        protected AltosGraphTime myAltosGraphTime(String suffix) {
+            return (new AltosGraphTime("Ascent " + suffix) {
+                public void addData(AltosDataPoint d) {
+                    int state = d.state();
+                    if (Altos.ao_flight_boost <= state && state <= Altos.ao_flight_coast) {
+                        super.addData(d);
+                    }
+                }
+            }).addElement(e_boost)
+              .addElement(e_fast)
+              .addElement(e_coast);
+        }
+    }
+    
+    static private class DescentGraphs extends OverallGraphs {
+        protected AltosGraphTime myAltosGraphTime(String suffix) {
+            return (new AltosGraphTime("Descent " + suffix) {
+                public void addData(AltosDataPoint d) {
+                    int state = d.state();
+                    if (Altos.ao_flight_drogue <= state && state <= Altos.ao_flight_main) {
+                        super.addData(d);
+                    }
+                }
+            }).addElement(e_drogue)
+              .addElement(e_main);
+            // ((XYGraph)graph[8]).ymin = new Double(-50);
+        }
+    }
+
+       public AltosGraphUI(AltosRecordIterable records, String name) throws InterruptedException, IOException {
+               super(String.format("Altos Graph %s", name));
+
+               AltosDataPointReader reader = new AltosDataPointReader (records);
+               if (reader == null)
+                       return;
+        
+               if (reader.has_accel)
+                   init(reader, records, 0);
+               else
+                   init(reader, records, 1);
+       }
+
+//    public AltosGraphUI(AltosDataPointReader data, int which)
+    //  {
+//        super("Altos Graph");
+//        init(data, which);
+//    }
+
+    private void init(AltosDataPointReader data, AltosRecordIterable records, int which) throws InterruptedException, IOException {
+       pane = new JTabbedPane();
+
+        AltosGraph graph = createGraph(data, which);
+
+        JFreeChart chart = graph.createChart();
+        ChartPanel chartPanel = new ChartPanel(chart);
+        chartPanel.setMouseWheelEnabled(true);
+        chartPanel.setPreferredSize(new java.awt.Dimension(800, 500));
+        pane.add(graph.title, chartPanel);
+
+       AltosFlightStatsTable stats = new AltosFlightStatsTable(new AltosFlightStats(records));
+       pane.add("Flight Statistics", stats);
+
+       setContentPane (pane);
+
+        pack();
+
+        RefineryUtilities.centerFrameOnScreen(this);
+
+        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
+        setVisible(true);
+    }
+
+    private static AltosGraph createGraph(Iterable<AltosDataPoint> data,
+            int which)
+    {
+        return createGraphsWhich(data, which).get(0);
+    }
+
+    private static ArrayList<AltosGraph> createGraphs(
+            Iterable<AltosDataPoint> data)
+    {
+        return createGraphsWhich(data, -1);
+    }
+
+    private static ArrayList<AltosGraph> createGraphsWhich(
+            Iterable<AltosDataPoint> data, int which)
+    {
+        ArrayList<AltosGraph> graph = new ArrayList<AltosGraph>();
+        graph.addAll((new OverallGraphs()).graphs());
+//        graph.addAll((new AscentGraphs()).graphs());
+//        graph.addAll((new DescentGraphs()).graphs());
+
+        if (which > 0) {
+            if (which >= graph.size()) {
+                which = 0;
+            }
+            AltosGraph g = graph.get(which);
+            graph = new ArrayList<AltosGraph>();
+            graph.add(g);
+        }
+
+        for (AltosDataPoint dp : data) {
+            for (AltosGraph g : graph) {
+                g.addData(dp);
+            }
+        }
+
+        return graph;
+    }
+}
+
+/* gnuplot bits...
+ *
+300x400
+
+--------------------------------------------------------
+TOO HARD! :)
+
+"ascent-gps-accuracy.png" "Vertical error margin to apogee - GPS v Baro (m)"
+    5:($7 < 6 ? $24-$11 : 1/0)
+"descent-gps-accuracy.png" "Vertical error margin during descent - GPS v Baro (m)"
+    5:($7 < 6 ? 1/0 : $24-$11)
+
+set output "overall-gps-accuracy.png"
+set ylabel "distance above sea level (m)"
+plot "telemetry.csv" using 5:11 with lines ti "baro altitude" axis x1y1, \
+    "telemetry.csv" using 5:24 with lines ti "gps altitude" axis x1y1
+
+set term png tiny size 700,700 enhanced
+set xlabel "m"
+set ylabel "m"
+set polar
+set grid polar
+set rrange[*:*]
+set angles degrees
+
+set output "overall-gps-path.png"
+#:30 with yerrorlines
+plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0) with lines ti "pad", \
+    "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0) with lines ti "boost", \
+    "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0) with lines ti "fast", \
+    "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0) with lines ti "coast", \
+    "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \
+    "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \
+    "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed"
+
+set output "ascent-gps-path.png"
+plot "telemetry.csv" using (90-$33):($7 == 2 ? $31 : 1/0):30 with lines ti "pad", \
+    "telemetry.csv" using (90-$33):($7 == 3 ? $31 : 1/0):20 with lines ti "boost", \
+    "telemetry.csv" using (90-$33):($7 == 4 ? $31 : 1/0):10 with lines ti "fast", \
+    "telemetry.csv" using (90-$33):($7 == 5 ? $31 : 1/0):5 with lines ti "coast"
+
+set output "descent-gps-path.png"
+plot "telemetry.csv" using (90-$33):($7 == 6 ? $31 : 1/0) with lines ti "drogue", \
+    "telemetry.csv" using (90-$33):($7 == 7 ? $31 : 1/0) with lines ti "main", \
+    "telemetry.csv" using (90-$33):($7 == 8 ? $31 : 1/0) with lines ti "landed"
+
+ */
+
+
diff --git a/altosui/AltosHexfile.java b/altosui/AltosHexfile.java
new file mode 100644 (file)
index 0000000..d52b46c
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * 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.io.*;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.Arrays;
+import org.altusmetrum.AltosLib.*;
+
+class HexFileInputStream extends PushbackInputStream {
+       public int line;
+
+       public HexFileInputStream(FileInputStream o) {
+               super(new BufferedInputStream(o));
+               line = 1;
+       }
+
+       public int read() throws IOException {
+               int     c = super.read();
+               if (c == '\n')
+                       line++;
+               return c;
+       }
+
+       public void unread(int c) throws IOException {
+               if (c == '\n')
+                       line--;
+               if (c != -1)
+                       super.unread(c);
+       }
+}
+
+class HexRecord implements Comparable {
+       public int      address;
+       public int      type;
+       public byte     checksum;
+       public byte[]   data;
+
+       static final int NORMAL = 0;
+       static final int EOF = 1;
+       static final int EXTENDED_ADDRESS = 2;
+
+       enum read_state {
+               marker,
+               length,
+               address,
+               type,
+               data,
+               checksum,
+               newline,
+               white,
+               done,
+       }
+
+       boolean ishex(int c) {
+               if ('0' <= c && c <= '9')
+                       return true;
+               if ('a' <= c && c <= 'f')
+                       return true;
+               if ('A' <= c && c <= 'F')
+                       return true;
+               return false;
+       }
+
+       boolean isspace(int c) {
+               switch (c) {
+               case ' ':
+               case '\t':
+                       return true;
+               }
+               return false;
+       }
+
+       int fromhex(int c) {
+               if ('0' <= c && c <= '9')
+                       return c - '0';
+               if ('a' <= c && c <= 'f')
+                       return c - 'a' + 10;
+               if ('A' <= c && c <= 'F')
+                       return c - 'A' + 10;
+               return -1;
+       }
+
+       public byte checksum() {
+               byte    got = 0;
+
+               got += data.length;
+               got += (address >> 8) & 0xff;
+               got += (address     ) & 0xff;
+               got += type;
+               for (int i = 0; i < data.length; i++)
+                       got += data[i];
+               return (byte) (-got);
+       }
+
+       public int compareTo(Object other) {
+               HexRecord       o = (HexRecord) other;
+               return address - o.address;
+       }
+
+       public String toString() {
+               return String.format("%04x: %02x (%d)", address, type, data.length);
+       }
+
+       public HexRecord(HexFileInputStream input) throws IOException {
+               read_state      state = read_state.marker;
+               int             nhexbytes = 0;
+               int             hex = 0;
+               int             ndata = 0;
+               byte            got_checksum;
+
+               while (state != read_state.done) {
+                       int c = input.read();
+                       if (c < 0 && state != read_state.white)
+                               throw new IOException(String.format("%d: Unexpected EOF", input.line));
+                       if (c == ' ')
+                               continue;
+                       switch (state) {
+                       case marker:
+                               if (c != ':')
+                                       throw new IOException("Missing ':'");
+                               state = read_state.length;
+                               nhexbytes = 2;
+                               hex = 0;
+                               break;
+                       case length:
+                       case address:
+                       case type:
+                       case data:
+                       case checksum:
+                               if(!ishex(c))
+                                       throw new IOException(String.format("Non-hex char '%c'", c));
+                               hex = hex << 4 | fromhex(c);
+                               --nhexbytes;
+                               if (nhexbytes != 0)
+                                       break;
+
+                               switch (state) {
+                               case length:
+                                       data = new byte[hex];
+                                       state = read_state.address;
+                                       nhexbytes = 4;
+                                       break;
+                               case address:
+                                       address = hex;
+                                       state = read_state.type;
+                                       nhexbytes = 2;
+                                       break;
+                               case type:
+                                       type = hex;
+                                       if (data.length > 0)
+                                               state = read_state.data;
+                                       else
+                                               state = read_state.checksum;
+                                       nhexbytes = 2;
+                                       ndata = 0;
+                                       break;
+                               case data:
+                                       data[ndata] = (byte) hex;
+                                       ndata++;
+                                       nhexbytes = 2;
+                                       if (ndata == data.length)
+                                               state = read_state.checksum;
+                                       break;
+                               case checksum:
+                                       checksum = (byte) hex;
+                                       state = read_state.newline;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               hex = 0;
+                               break;
+                       case newline:
+                               if (c != '\n' && c != '\r')
+                                       throw new IOException("Missing newline");
+                               state = read_state.white;
+                               break;
+                       case white:
+                               if (!isspace(c)) {
+                                       input.unread(c);
+                                       state = read_state.done;
+                               }
+                               break;
+                       case done:
+                               break;
+                       }
+               }
+               got_checksum = checksum();
+               if (got_checksum != checksum)
+                       throw new IOException(String.format("Invalid checksum (read 0x%02x computed 0x%02x)\n",
+                                                           checksum, got_checksum));
+       }
+}
+
+public class AltosHexfile {
+       public int      address;
+       public byte[]   data;
+
+       public byte get_byte(int a) {
+               return data[a - address];
+       }
+
+       public AltosHexfile(FileInputStream file) throws IOException {
+               HexFileInputStream      input = new HexFileInputStream(file);
+               LinkedList<HexRecord>   record_list = new LinkedList<HexRecord>();
+               boolean                 done = false;
+
+               while (!done) {
+                       HexRecord       record = new HexRecord(input);
+
+                       if (record.type == HexRecord.EOF)
+                               done = true;
+                       else
+                               record_list.add(record);
+               }
+               HexRecord[] records  = record_list.toArray(new HexRecord[0]);
+               Arrays.sort(records);
+               if (records.length > 0) {
+                       int     base = records[0].address;
+                       int     bound = records[records.length-1].address +
+                               records[records.length-1].data.length;
+
+                       data = new byte[bound - base];
+                       address = base;
+                       Arrays.fill(data, (byte) 0xff);
+
+                       /* Paint the records into the new array */
+                       for (int i = 0; i < records.length; i++) {
+                               for (int j = 0; j < records[i].data.length; j++)
+                                       data[records[i].address - base + j] = records[i].data[j];
+                       }
+               }
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosIdleMonitorUI.java b/altosui/AltosIdleMonitorUI.java
new file mode 100644 (file)
index 0000000..46ca3e5
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosIdleMonitorUI extends AltosFrame implements AltosFlightDisplay, AltosFontListener, AltosIdleMonitorListener {
+       AltosDevice             device;
+       JTabbedPane             pane;
+       AltosPad                pad;
+       AltosInfoTable          flightInfo;
+       AltosFlightStatus       flightStatus;
+       AltosIdleMonitor        thread;
+       int                     serial;
+       boolean                 remote;
+
+       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();
+               flightInfo.clear();
+       }
+
+       public void set_font() {
+               pad.set_font();
+               flightInfo.set_font();
+       }
+
+       public void font_size_changed(int font_size) {
+               set_font();
+       }
+
+       AltosFlightStatusUpdate status_update;
+
+       public void show(AltosState state, int crc_errors) {
+               status_update.saved_state = state;
+               try {
+                       pad.show(state, crc_errors);
+                       flightStatus.show(state, crc_errors);
+                       flightInfo.show(state, crc_errors);
+               } catch (Exception e) {
+                       System.out.print("Show exception" + e);
+               }
+       }
+
+       public void update(final AltosState state) {
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       show(state, 0);
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       Container       bag;
+       AltosFreqList   frequencies;
+
+       public AltosIdleMonitorUI(JFrame in_owner)
+               throws FileNotFoundException, AltosSerialInUseException, TimeoutException, InterruptedException {
+
+               device = AltosDeviceDialog.show(in_owner, Altos.product_any);
+               remote = false;
+               if (!device.matchProduct(Altos.product_altimeter))
+                       remote = true;
+
+               serial = device.getSerial();
+               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", device.toShortString()));
+
+               /* Stick frequency selector at top of table for telemetry monitoring */
+               if (remote && serial >= 0) {
+                       // Frequency menu
+                       frequencies = new AltosFreqList(AltosUIPreferences.frequency(serial));
+                       frequencies.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               double frequency = frequencies.frequency();
+                                               thread.set_frequency(frequency);
+                                               AltosUIPreferences.set_frequency(device.getSerial(),
+                                                                              frequency);
+                                       }
+                       });
+                       c.gridx = 0;
+                       c.gridy = 0;
+                       c.insets = new Insets(3, 3, 3, 3);
+                       c.anchor = GridBagConstraints.WEST;
+                       bag.add (frequencies, c);
+               }
+
+
+               /* Flight status is always visible */
+               flightStatus = new AltosFlightStatus();
+               c.gridx = 0;
+               c.gridy = 1;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.gridwidth = 2;
+               bag.add(flightStatus, c);
+               c.gridwidth = 1;
+
+               /* 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);
+
+               flightInfo = new AltosInfoTable();
+               pane.add("Table", new JScrollPane(flightInfo));
+
+               /* 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;
+               c.gridwidth = 2;
+               bag.add(pane, c);
+
+               setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+
+               AltosUIPreferences.register_font_listener(this);
+
+               addWindowListener(new WindowAdapter() {
+                               @Override
+                               public void windowClosing(WindowEvent e) {
+                                       disconnect();
+                                       setVisible(false);
+                                       dispose();
+                                       AltosUIPreferences.unregister_font_listener(AltosIdleMonitorUI.this);
+                               }
+                       });
+
+               pack();
+               setVisible(true);
+
+               thread = new AltosIdleMonitor((AltosIdleMonitorListener) this, (AltosLink) new AltosSerial (device), (boolean) remote);
+
+               status_update = new AltosFlightStatusUpdate(flightStatus);
+
+               new javax.swing.Timer(100, status_update).start();
+
+               thread.start();
+       }
+}
diff --git a/altosui/AltosIgniteUI.java b/altosui/AltosIgniteUI.java
new file mode 100644 (file)
index 0000000..78eba8e
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosIgniteUI
+       extends AltosDialog
+       implements ActionListener
+{
+       AltosDevice     device;
+       JFrame          owner;
+       JLabel          label;
+       JRadioButton    apogee;
+       JLabel          apogee_status_label;
+       JRadioButton    main;
+       JLabel          main_status_label;
+       JToggleButton   arm;
+       JButton         fire;
+       javax.swing.Timer       timer;
+       JButton         close;
+
+       int             apogee_status;
+       int             main_status;
+
+       final static int        timeout = 1 * 1000;
+
+       int             time_remaining;
+       boolean         timer_running;
+
+       LinkedBlockingQueue<String>     command_queue;
+
+       class IgniteHandler implements Runnable {
+               AltosIgnite     ignite;
+               JFrame          owner;
+
+               void send_exception(Exception e) {
+                       final Exception f_e = e;
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               ignite_exception(f_e);
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+
+               public void run () {
+                       try {
+                               AltosSerial     serial = new AltosSerial(device);
+                               serial.set_frame(owner);
+                               ignite = new AltosIgnite(serial,
+                                                        !device.matchProduct(Altos.product_altimeter));
+
+                       } catch (Exception e) {
+                               send_exception(e);
+                               return;
+                       }
+
+                       for (;;) {
+                               Runnable        r;
+
+                               try {
+                                       String          command = command_queue.take();
+                                       String          reply = null;
+
+                                       if (command.equals("get_status")) {
+                                               apogee_status = ignite.status(AltosIgnite.Apogee);
+                                               main_status = ignite.status(AltosIgnite.Main);
+                                               reply = "status";
+                                       } else if (command.equals("main")) {
+                                               ignite.fire(AltosIgnite.Main);
+                                               reply = "fired";
+                                       } else if (command.equals("apogee")) {
+                                               ignite.fire(AltosIgnite.Apogee);
+                                               reply = "fired";
+                                       } else if (command.equals("quit")) {
+                                               ignite.close();
+                                               break;
+                                       } else {
+                                               throw new ParseException(String.format("invalid command %s", command), 0);
+                                       }
+                                       final String f_reply = reply;
+                                       r = new Runnable() {
+                                                       public void run() {
+                                                               ignite_reply(f_reply);
+                                                       }
+                                               };
+                                       SwingUtilities.invokeLater(r);
+                               } catch (Exception e) {
+                                       send_exception(e);
+                               }
+                       }
+               }
+
+               public IgniteHandler(JFrame in_owner) {
+                       owner = in_owner;
+               }
+       }
+
+       void ignite_exception(Exception e) {
+               if (e instanceof FileNotFoundException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     ((FileNotFoundException) e).getMessage(),
+                                                     "Cannot open target device",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof AltosSerialInUseException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Device \"%s\" already in use",
+                                                                   device.toShortString()),
+                                                     "Device in use",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof IOException) {
+                       IOException ee = (IOException) e;
+                       JOptionPane.showMessageDialog(owner,
+                                                     device.toShortString(),
+                                                     ee.getLocalizedMessage(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Connection to \"%s\" failed",
+                                                                   device.toShortString()),
+                                                     "Connection Failed",
+                                                     JOptionPane.ERROR_MESSAGE);
+               }
+               close();
+       }
+
+       void ignite_reply(String reply) {
+               if (reply.equals("status")) {
+                       set_ignite_status();
+               } else if (reply.equals("fired")) {
+                       fired();
+               }
+       }
+
+       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 send_command(String command) {
+               try {
+                       command_queue.put(command);
+               } catch (Exception ex) {
+                       ignite_exception(ex);
+               }
+       }
+
+       boolean getting_status = false;
+
+       boolean visible = false;
+       void set_ignite_status() {
+               getting_status = false;
+               apogee_status_label.setText(String.format("\"%s\"", AltosIgnite.status_string(apogee_status)));
+               main_status_label.setText(String.format("\"%s\"", AltosIgnite.status_string(main_status)));
+               if (!visible) {
+                       visible = true;
+                       setVisible(true);
+               }
+       }
+
+       void poll_ignite_status() {
+               if (!getting_status) {
+                       getting_status = true;
+                       send_command("get_status");
+               }
+       }
+
+       boolean firing = false;
+
+       void start_fire(String which) {
+               if (!firing) {
+                       firing = true;
+                       send_command(which);
+               }
+       }
+
+       void fired() {
+               firing = false;
+               cancel();
+       }
+
+       void close() {
+               send_command("quit");
+               timer.stop();
+               setVisible(false);
+               dispose();
+       }
+
+       void tick_timer() {
+               if (timer_running) {
+                       --time_remaining;
+                       if (time_remaining <= 0)
+                               cancel();
+                       else
+                               set_arm_text();
+               }
+               poll_ignite_status();
+       }
+
+       void fire() {
+               if (arm.isEnabled() && arm.isSelected() && time_remaining > 0) {
+                       String  igniter = "none";
+                       if (apogee.isSelected() && !main.isSelected())
+                               igniter = "apogee";
+                       else if (main.isSelected() && !apogee.isSelected())
+                               igniter = "main";
+                       send_command(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() {
+               command_queue = new LinkedBlockingQueue<String>();
+
+               device = AltosDeviceDialog.show(owner, Altos.product_any);
+               if (device != null) {
+                               IgniteHandler   handler = new IgniteHandler(owner);
+                               Thread          t = new Thread(handler);
+                               t.start();
+                               return true;
+               }
+               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 = 0;
+               c.weighty = 0;
+
+               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);
+
+               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");
+
+               c.gridx = 0;
+               c.gridy = 4;
+               c.gridwidth = 2;
+               c.anchor = GridBagConstraints.CENTER;
+               close = new JButton ("Close");
+               pane.add(close, c);
+               close.addActionListener(this);
+               close.setActionCommand("close");
+                       
+               pack();
+               setLocationRelativeTo(owner);
+
+               addWindowListener(new ConfigListener(this));
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosInfoTable.java b/altosui/AltosInfoTable.java
new file mode 100644 (file)
index 0000000..c140097
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosInfoTable extends JTable {
+       private AltosFlightInfoTableModel model;
+
+       static final int info_columns = 3;
+       static final int info_rows = 17;
+
+       int desired_row_height() {
+               FontMetrics     infoValueMetrics = getFontMetrics(Altos.table_value_font);
+               return (infoValueMetrics.getHeight() + infoValueMetrics.getLeading()) * 18 / 10;
+       }
+
+       int text_width(String t) {
+               FontMetrics     infoValueMetrics = getFontMetrics(Altos.table_value_font);
+
+               return infoValueMetrics.stringWidth(t);
+       }
+
+       void set_layout() {
+               setRowHeight(desired_row_height());
+               for (int i = 0; i < info_columns * 2; i++)
+               {
+                       TableColumn column = getColumnModel().getColumn(i);
+
+                       if ((i & 1) == 0)
+                               column.setPreferredWidth(text_width(" Satellites Visible "));
+                       else
+                               column.setPreferredWidth(text_width(" 179°59.99999' "));
+               }
+       }
+
+       public AltosInfoTable() {
+               super(new AltosFlightInfoTableModel(info_rows, info_columns));
+               model = (AltosFlightInfoTableModel) getModel();
+               setFont(Altos.table_value_font);
+               setAutoResizeMode(AUTO_RESIZE_ALL_COLUMNS);
+               setShowGrid(true);
+               set_layout();
+               doLayout();
+       }
+
+       public void set_font() {
+               setFont(Altos.table_value_font);
+               set_layout();
+               doLayout();
+       }
+
+       public Dimension getPreferredScrollableViewportSize() {
+               return getPreferredSize();
+       }
+
+       void info_reset() {
+               model.reset();
+       }
+
+       void info_add_row(int col, String name, String value) {
+               model.addRow(col, name, value);
+       }
+
+       void info_add_row(int col, String name, String format, Object... parameters) {
+               info_add_row (col, name, String.format(format, parameters));
+       }
+
+       void info_add_deg(int col, String name, double v, int pos, int neg) {
+               int     c = pos;
+               if (v < 0) {
+                       c = neg;
+                       v = -v;
+               }
+               double  deg = Math.floor(v);
+               double  min = (v - deg) * 60;
+
+               info_add_row(col, name, String.format("%3.0f°%08.5f'", deg, min));
+       }
+
+       void info_finish() {
+               model.finish();
+       }
+
+       public void clear() {
+               model.clear();
+       }
+
+       public void show(AltosState state, int crc_errors) {
+               if (state == null)
+                       return;
+               info_reset();
+               info_add_row(0, "Altitude", "%6.0f    m", state.altitude);
+               info_add_row(0, "Pad altitude", "%6.0f    m", state.ground_altitude);
+               info_add_row(0, "Height", "%6.0f    m", state.height);
+               info_add_row(0, "Max height", "%6.0f    m", state.max_height);
+               info_add_row(0, "Acceleration", "%8.1f  m/s²", state.acceleration);
+               info_add_row(0, "Max acceleration", "%8.1f  m/s²", state.max_acceleration);
+               info_add_row(0, "Speed", "%8.1f  m/s", state.ascent ? state.speed : state.baro_speed);
+               info_add_row(0, "Max Speed", "%8.1f  m/s", state.max_speed);
+               info_add_row(0, "Temperature", "%9.2f °C", state.temperature);
+               info_add_row(0, "Battery", "%9.2f V", state.battery);
+               if (state.drogue_sense != AltosRecord.MISSING)
+                       info_add_row(0, "Drogue", "%9.2f V", state.drogue_sense);
+               if (state.main_sense != AltosRecord.MISSING)
+                       info_add_row(0, "Main", "%9.2f V", state.main_sense);
+               info_add_row(0, "CRC Errors", "%6d", crc_errors);
+
+               if (state.gps == null || !state.gps.connected) {
+                       info_add_row(1, "GPS", "not available");
+               } else {
+                       if (state.gps_ready)
+                               info_add_row(1, "GPS state", "%s", "ready");
+                       else
+                               info_add_row(1, "GPS state", "wait (%d)",
+                                            state.gps_waiting);
+                       if (state.data.gps.locked)
+                               info_add_row(1, "GPS", "   locked");
+                       else if (state.data.gps.connected)
+                               info_add_row(1, "GPS", " unlocked");
+                       else
+                               info_add_row(1, "GPS", "  missing");
+                       info_add_row(1, "Satellites", "%6d", state.data.gps.nsat);
+                       info_add_deg(1, "Latitude", state.gps.lat, 'N', 'S');
+                       info_add_deg(1, "Longitude", state.gps.lon, 'E', 'W');
+                       info_add_row(1, "GPS altitude", "%6d", state.gps.alt);
+                       info_add_row(1, "GPS height", "%6.0f", state.gps_height);
+
+                       /* The SkyTraq GPS doesn't report these values */
+                       if (false) {
+                               info_add_row(1, "GPS ground speed", "%8.1f m/s %3d°",
+                                            state.gps.ground_speed,
+                                            state.gps.course);
+                               info_add_row(1, "GPS climb rate", "%8.1f m/s",
+                                            state.gps.climb_rate);
+                               info_add_row(1, "GPS error", "%6d m(h)%3d m(v)",
+                                            state.gps.h_error, state.gps.v_error);
+                       }
+                       info_add_row(1, "GPS hdop", "%8.1f", state.gps.hdop);
+
+                       if (state.npad > 0) {
+                               if (state.from_pad != null) {
+                                       info_add_row(1, "Distance from pad", "%6d m",
+                                                    (int) (state.from_pad.distance + 0.5));
+                                       info_add_row(1, "Direction from pad", "%6d°",
+                                                    (int) (state.from_pad.bearing + 0.5));
+                                       info_add_row(1, "Elevation from pad", "%6d°",
+                                                    (int) (state.elevation + 0.5));
+                                       info_add_row(1, "Range from pad", "%6d m",
+                                                    (int) (state.range + 0.5));
+                               } else {
+                                       info_add_row(1, "Distance from pad", "unknown");
+                                       info_add_row(1, "Direction from pad", "unknown");
+                                       info_add_row(1, "Elevation from pad", "unknown");
+                                       info_add_row(1, "Range from pad", "unknown");
+                               }
+                               info_add_deg(1, "Pad latitude", state.pad_lat, 'N', 'S');
+                               info_add_deg(1, "Pad longitude", state.pad_lon, 'E', 'W');
+                               info_add_row(1, "Pad GPS alt", "%6.0f m", state.pad_alt);
+                       }
+                       info_add_row(1, "GPS date", "%04d-%02d-%02d",
+                                      state.gps.year,
+                                      state.gps.month,
+                                      state.gps.day);
+                       info_add_row(1, "GPS time", "  %02d:%02d:%02d",
+                                      state.gps.hour,
+                                      state.gps.minute,
+                                      state.gps.second);
+                       int     nsat_vis = 0;
+                       int     c;
+
+                       if (state.gps.cc_gps_sat == null)
+                               info_add_row(2, "Satellites Visible", "%4d", 0);
+                       else {
+                               info_add_row(2, "Satellites Visible", "%4d", state.gps.cc_gps_sat.length);
+                               for (c = 0; c < state.gps.cc_gps_sat.length; c++) {
+                                       info_add_row(2, "Satellite id,C/N0",
+                                                    "%4d, %4d",
+                                                    state.gps.cc_gps_sat[c].svid,
+                                                    state.gps.cc_gps_sat[c].c_n0);
+                               }
+                       }
+               }
+               info_finish();
+       }
+}
diff --git a/altosui/AltosKML.java b/altosui/AltosKML.java
new file mode 100644 (file)
index 0000000..ff0734b
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * 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.io.*;
+import java.text.*;
+import java.util.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosKML implements AltosWriter {
+
+       File                    name;
+       PrintStream             out;
+       int                     state = -1;
+       AltosRecord             prev = null;
+
+       static final String[] kml_state_colors = {
+               "FF000000",
+               "FF000000",
+               "FF000000",
+               "FF0000FF",
+               "FF4080FF",
+               "FF00FFFF",
+               "FFFF0000",
+               "FF00FF00",
+               "FF000000",
+               "FFFFFFFF"
+       };
+
+       static final String kml_header_start =
+               "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+               "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n" +
+               "<Document>\n" +
+               "  <name>AO Flight#%d S/N: %03d</name>\n" +
+               "  <description>\n";
+       static final String kml_header_end =
+               "  </description>\n" +
+               "  <open>0</open>\n";
+
+       static final String kml_style_start =
+               "  <Style id=\"ao-flightstate-%s\">\n" +
+               "    <LineStyle><color>%s</color><width>4</width></LineStyle>\n" +
+               "    <BalloonStyle>\n" +
+               "      <text>\n";
+
+       static final String kml_style_end =
+               "      </text>\n" +
+               "    </BalloonStyle>\n" +
+               "  </Style>\n";
+
+       static final String kml_placemark_start =
+               "  <Placemark>\n" +
+               "    <name>%s</name>\n" +
+               "    <styleUrl>#ao-flightstate-%s</styleUrl>\n" +
+               "    <LineString>\n" +
+               "      <tessellate>1</tessellate>\n" +
+               "      <altitudeMode>absolute</altitudeMode>\n" +
+               "      <coordinates>\n";
+
+       static final String kml_coord_fmt =
+       "        %.7f,%.7f,%.7f <!-- alt %12.7f time %12.7f sats %d -->\n";
+
+       static final String kml_placemark_end =
+               "      </coordinates>\n" +
+               "    </LineString>\n" +
+               "  </Placemark>\n";
+
+       static final String kml_footer =
+               "</Document>\n" +
+               "</kml>\n";
+
+       void start (AltosRecord record) {
+               out.printf(kml_header_start, record.flight, record.serial);
+               out.printf("Date:   %04d-%02d-%02d\n",
+                          record.gps.year, record.gps.month, record.gps.day);
+               out.printf("Time:     %2d:%02d:%02d\n",
+                          record.gps.hour, record.gps.minute, record.gps.second);
+               out.printf("%s", kml_header_end);
+       }
+
+       boolean started = false;
+
+       void state_start(AltosRecord record) {
+               String  state_name = Altos.state_name(record.state);
+               out.printf(kml_style_start, state_name, kml_state_colors[record.state]);
+               out.printf("\tState: %s\n", state_name);
+               out.printf("%s", kml_style_end);
+               out.printf(kml_placemark_start, state_name, state_name);
+       }
+
+       void state_end(AltosRecord record) {
+               out.printf("%s", kml_placemark_end);
+       }
+
+       void coord(AltosRecord record) {
+               AltosGPS        gps = record.gps;
+               out.printf(kml_coord_fmt,
+                          gps.lon, gps.lat,
+                          record.filtered_altitude(), (double) gps.alt,
+                          record.time, gps.nsat);
+       }
+
+       void end() {
+               out.printf("%s", kml_footer);
+       }
+
+       public void close() {
+               if (prev != null) {
+                       state_end(prev);
+                       end();
+                       prev = null;
+               }
+       }
+
+       public void write(AltosRecord record) {
+               AltosGPS        gps = record.gps;
+
+               if (gps == null)
+                       return;
+               if ((record.seen & (AltosRecord.seen_flight)) == 0)
+                       return;
+               if ((record.seen & (AltosRecord.seen_gps_lat)) == 0)
+                       return;
+               if ((record.seen & (AltosRecord.seen_gps_lon)) == 0)
+                       return;
+               if (!started) {
+                       start(record);
+                       started = true;
+               }
+               if (prev != null &&
+                   prev.gps.second == record.gps.second &&
+                   prev.gps.minute == record.gps.minute &&
+                   prev.gps.hour == record.gps.hour)
+                       return;
+               if (record.state != state) {
+                       state = record.state;
+                       if (prev != null) {
+                               coord(record);
+                               state_end(prev);
+                       }
+                       state_start(record);
+               }
+               coord(record);
+               prev = record;
+       }
+
+       public void write(AltosRecordIterable iterable) {
+               for (AltosRecord record : iterable)
+                       write(record);
+       }
+
+       public AltosKML(File in_name) throws FileNotFoundException {
+               name = in_name;
+               out = new PrintStream(name);
+       }
+
+       public AltosKML(String in_string) throws FileNotFoundException {
+               this(new File(in_string));
+       }
+}
diff --git a/altosui/AltosLanded.java b/altosui/AltosLanded.java
new file mode 100644 (file)
index 0000000..a47e1cb
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosLanded extends JComponent implements AltosFlightDisplay, ActionListener {
+       GridBagLayout   layout;
+
+       public class LandedValue {
+               JLabel          label;
+               JTextField      value;
+               void show(AltosState state, int crc_errors) {}
+
+               void reset() {
+                       value.setText("");
+               }
+
+               void show() {
+                       label.setVisible(true);
+                       value.setVisible(true);
+               }
+
+               public void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+               }
+
+               void hide() {
+                       label.setVisible(false);
+                       value.setVisible(false);
+               }
+
+               void show(String format, double v) {
+                       show();
+                       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(Altos.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(Altos.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) {
+                       show();
+                       if (state.gps != null && state.gps.connected)
+                               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) {
+                       show();
+                       if (state.gps != null && state.gps.connected)
+                               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) {
+                       show();
+                       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) {
+                       show();
+                       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 set_font() {
+               lat.set_font();
+               lon.set_font();
+               bearing.set_font();
+               distance.set_font();
+               height.set_font();
+               speed.set_font();
+               accel.set_font();
+       }
+
+       public void show(AltosState state, int crc_errors) {
+               if (state.gps != null && state.gps.connected) {
+                       bearing.show(state, crc_errors);
+                       distance.show(state, crc_errors);
+                       lat.show(state, crc_errors);
+                       lon.show(state, crc_errors);
+               } else {
+                       bearing.hide();
+                       distance.hide();
+                       lat.hide();
+                       lon.hide();
+               }
+               height.show(state, crc_errors);
+               speed.show(state, crc_errors);
+               accel.show(state, crc_errors);
+               if (reader.backing_file() != null)
+                       graph.setEnabled(true);
+       }
+
+       JButton graph;
+       AltosFlightReader reader;
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("graph")) {
+                       File    file = reader.backing_file();
+                       if (file != null) {
+                               String  filename = file.getName();
+                               try {
+                                       AltosRecordIterable records = null;
+                                       if (filename.endsWith("eeprom")) {
+                                               FileInputStream in = new FileInputStream(file);
+                                               records = new AltosEepromIterable(in);
+                                       } else if (filename.endsWith("telem")) {
+                                               FileInputStream in = new FileInputStream(file);
+                                               records = new AltosTelemetryIterable(in);
+                                       } else {
+                                               throw new FileNotFoundException(filename);
+                                       }
+                                       try {
+                                               new AltosGraphUI(records, filename);
+                                       } catch (InterruptedException ie) {
+                                       } catch (IOException ie) {
+                                       }
+                               } catch (FileNotFoundException fe) {
+                                       JOptionPane.showMessageDialog(null,
+                                                                     fe.getMessage(),
+                                                                     "Cannot open file",
+                                                                     JOptionPane.ERROR_MESSAGE);
+                               }
+                       }
+               }
+       }
+
+       public AltosLanded(AltosFlightReader in_reader) {
+               layout = new GridBagLayout();
+
+               reader = in_reader;
+
+               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);
+
+               graph = new JButton ("Graph Flight");
+               graph.setActionCommand("graph");
+               graph.addActionListener(this);
+               graph.setEnabled(false);
+
+               GridBagConstraints      c = new GridBagConstraints();
+
+               c.gridx = 0; c.gridy = 7;
+               c.insets = new Insets(10, 10, 10, 10);
+               c.anchor = GridBagConstraints.WEST;
+               c.weightx = 0;
+               c.weighty = 0;
+               c.fill = GridBagConstraints.VERTICAL;
+               add(graph, c);
+       }
+}
diff --git a/altosui/AltosLaunch.java b/altosui/AltosLaunch.java
new file mode 100644 (file)
index 0000000..0e493b9
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * 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.*;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.*;
+import javax.swing.event.*;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosLaunch {
+       AltosDevice     device;
+       AltosSerial     serial;
+       boolean         serial_started;
+       int             launcher_serial;
+       int             launcher_channel;
+       int             rssi;
+
+       final static int        Unknown = -1;
+       final static int        Good = 0;
+       final static int        Bad = 1;
+
+       int             armed;
+       int             igniter;
+
+       private void start_serial() throws InterruptedException {
+               serial_started = true;
+       }
+
+       private void stop_serial() throws InterruptedException {
+               if (!serial_started)
+                       return;
+               serial_started = false;
+               if (serial == null)
+                       return;
+       }
+
+       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;
+               }
+       }
+
+       public boolean status() throws InterruptedException, TimeoutException {
+               boolean ok = false;
+               if (serial == null)
+                       return false;
+               string_ref status_name = new string_ref();
+               start_serial();
+               serial.printf("l %d %d\n", launcher_serial, launcher_channel);
+               for (;;) {
+                       String line = serial.get_reply(20000);
+                       if (line == null)
+                               throw new TimeoutException();
+                       if (get_string(line, "Rssi: ", status_name)) {
+                               try {
+                                       rssi = Altos.fromdec(status_name.get());
+                               } catch (NumberFormatException ne) {
+                               }
+                               break;
+                       } else if (get_string(line, "Armed: ", status_name)) {
+                               armed = Good;
+                               String status = status_name.get();
+                               if (status.startsWith("igniter good"))
+                                       igniter = Good;
+                               else if (status.startsWith("igniter bad"))
+                                       igniter = Bad;
+                               else
+                                       igniter = Unknown;
+                               ok = true;
+                       } else if (get_string(line, "Disarmed: ", status_name)) {
+                               armed = Bad;
+                               if (status_name.get().startsWith("igniter good"))
+                                       igniter = Good;
+                               else if (status_name.get().startsWith("igniter bad"))
+                                       igniter = Bad;
+                               else
+                                       igniter = Unknown;
+                               ok = true;
+                       } else if (get_string(line, "Error ", status_name)) {
+                               armed = Unknown;
+                               igniter = Unknown;
+                               ok = false;
+                               break;
+                       }
+               }
+               stop_serial();
+               if (!ok) {
+                       armed = Unknown;
+                       igniter = Unknown;
+               }
+               return ok;
+       }
+
+       public static String status_string(int status) {
+               switch (status) {
+               case Good:
+                       return "good";
+               case Bad:
+                       return "open";
+               }
+               return "unknown";
+       }
+
+       public void arm() {
+               if (serial == null)
+                       return;
+               try {
+                       start_serial();
+                       serial.printf("a %d %d\n", launcher_serial, launcher_channel);
+                       serial.flush_output();
+               } catch (InterruptedException ie) {
+               } finally {
+                       try {
+                               stop_serial();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       public void fire() {
+               if (serial == null)
+                       return;
+               try {
+                       start_serial();
+                       serial.printf("i %d %d\n", launcher_serial, launcher_channel);
+                       serial.flush_output();
+               } catch (InterruptedException ie) {
+               } finally {
+                       try {
+                               stop_serial();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       public void close() {
+               try {
+                       stop_serial();
+               } catch (InterruptedException ie) {
+               }
+               serial.close();
+               serial = null;
+       }
+
+       public void set_frame(Frame frame) {
+               serial.set_frame(frame);
+       }
+
+       public void set_remote(int in_serial, int in_channel) {
+               launcher_serial = in_serial;
+               launcher_channel = in_channel;
+       }
+
+       public AltosLaunch(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException {
+
+               device = in_device;
+               serial = new AltosSerial(device);
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosLaunchUI.java b/altosui/AltosLaunchUI.java
new file mode 100644 (file)
index 0000000..4448154
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+class FireButton extends JButton {
+       protected void processMouseEvent(MouseEvent e) {
+               super.processMouseEvent(e);
+               switch (e.getID()) {
+               case MouseEvent.MOUSE_PRESSED:
+                       if (actionListener != null)
+                               actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_down"));
+                       break;
+               case MouseEvent.MOUSE_RELEASED:
+                       if (actionListener != null)
+                               actionListener.actionPerformed(new ActionEvent(this, e.getID(), "fire_up"));
+                       break;
+               }
+       }
+
+       public FireButton(String s) {
+               super(s);
+       }
+}
+
+public class AltosLaunchUI
+       extends AltosDialog
+       implements ActionListener
+{
+       AltosDevice     device;
+       JFrame          owner;
+       JLabel          label;
+
+       int             radio_channel;
+       JLabel          radio_channel_label;
+       JTextField      radio_channel_text;
+
+       int             launcher_serial;
+       JLabel          launcher_serial_label;
+       JTextField      launcher_serial_text;
+
+       int             launcher_channel;
+       JLabel          launcher_channel_label;
+       JTextField      launcher_channel_text;
+
+       JLabel          armed_label;
+       JLabel          armed_status_label;
+       JLabel          igniter;
+       JLabel          igniter_status_label;
+       JToggleButton   arm;
+       FireButton      fire;
+       javax.swing.Timer       arm_timer;
+       javax.swing.Timer       fire_timer;
+
+       boolean         firing;
+       boolean         armed;
+       int             armed_status;
+       int             igniter_status;
+       int             rssi;
+
+       final static int        arm_timeout = 1 * 1000;
+       final static int        fire_timeout = 250;
+
+       int             armed_count;
+
+       LinkedBlockingQueue<String>     command_queue;
+
+       class LaunchHandler implements Runnable {
+               AltosLaunch     launch;
+               JFrame          owner;
+
+               void send_exception(Exception e) {
+                       final Exception f_e = e;
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               launch_exception(f_e);
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+               }
+
+               public void run () {
+                       try {
+                               launch = new AltosLaunch(device);
+                       } catch (Exception e) {
+                               send_exception(e);
+                               return;
+                       }
+                       launch.set_frame(owner);
+                       launch.set_remote(launcher_serial, launcher_channel);
+
+                       for (;;) {
+                               Runnable        r;
+
+                               try {
+                                       String          command = command_queue.take();
+                                       String          reply = null;
+
+                                       if (command.equals("get_status")) {
+                                               launch.status();
+                                               reply = "status";
+                                               armed_status = launch.armed;
+                                               igniter_status = launch.igniter;
+                                               rssi = launch.rssi;
+                                       } else if (command.equals("set_remote")) {
+                                               launch.set_remote(launcher_serial, launcher_channel);
+                                               reply = "remote set";
+                                       } else if (command.equals("arm")) {
+                                               launch.arm();
+                                               reply = "armed";
+                                       } else if (command.equals("fire")) {
+                                               launch.fire();
+                                               reply = "fired";
+                                       } else if (command.equals("quit")) {
+                                               launch.close();
+                                               break;
+                                       } else {
+                                               throw new ParseException(String.format("invalid command %s", command), 0);
+                                       }
+                                       final String f_reply = reply;
+                                       r = new Runnable() {
+                                                       public void run() {
+                                                               launch_reply(f_reply);
+                                                       }
+                                               };
+                                       SwingUtilities.invokeLater(r);
+                               } catch (Exception e) {
+                                       send_exception(e);
+                               }
+                       }
+               }
+
+               public LaunchHandler(JFrame in_owner) {
+                       owner = in_owner;
+               }
+       }
+
+       void launch_exception(Exception e) {
+               if (e instanceof FileNotFoundException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     ((FileNotFoundException) e).getMessage(),
+                                                     "Cannot open target device",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof AltosSerialInUseException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Device \"%s\" already in use",
+                                                                   device.toShortString()),
+                                                     "Device in use",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof IOException) {
+                       IOException ee = (IOException) e;
+                       JOptionPane.showMessageDialog(owner,
+                                                     device.toShortString(),
+                                                     ee.getLocalizedMessage(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Connection to \"%s\" failed",
+                                                                   device.toShortString()),
+                                                     "Connection Failed",
+                                                     JOptionPane.ERROR_MESSAGE);
+               }
+               close();
+       }
+
+       void launch_reply(String reply) {
+               if (reply == null)
+                       return;
+               if (reply.equals("remote set"))
+                       poll_launch_status();
+               if (reply.equals("status")) {
+                       set_launch_status();
+               }
+       }
+
+       void set_arm_text() {
+               if (arm.isSelected())
+                       arm.setText(String.format("%d", armed_count));
+               else
+                       arm.setText("Arm");
+       }
+
+       void start_arm_timer() {
+               armed_count = 30;
+               set_arm_text();
+       }
+
+       void stop_arm_timer() {
+               armed_count = 0;
+               armed = false;
+               arm.setSelected(false);
+               fire.setEnabled(false);
+               set_arm_text();
+       }
+
+       void cancel () {
+               fire.setEnabled(false);
+               firing = false;
+               stop_arm_timer();
+       }
+
+       void send_command(String command) {
+               try {
+                       command_queue.put(command);
+               } catch (Exception ex) {
+                       launch_exception(ex);
+               }
+       }
+
+       boolean getting_status = false;
+
+       void set_launch_status() {
+               getting_status = false;
+               armed_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(armed_status)));
+               igniter_status_label.setText(String.format("\"%s\"", AltosLaunch.status_string(igniter_status)));
+       }
+
+       void poll_launch_status() {
+               if (!getting_status && !firing && !armed) {
+                       getting_status = true;
+                       send_command("get_status");
+               }
+       }
+
+       void fired() {
+               firing = false;
+               cancel();
+       }
+
+       void close() {
+               send_command("quit");
+               arm_timer.stop();
+               setVisible(false);
+               dispose();
+       }
+
+       void tick_arm_timer() {
+               if (armed_count > 0) {
+                       --armed_count;
+                       if (armed_count <= 0) {
+                               armed_count = 0;
+                               cancel();
+                       } else {
+                               if (!firing) {
+                                       send_command("arm");
+                                       set_arm_text();
+                               }
+                       }
+               }
+               poll_launch_status();
+       }
+
+       void arm() {
+               if (arm.isSelected()) {
+                       fire.setEnabled(true);
+                       start_arm_timer();
+                       if (!firing)
+                               send_command("arm");
+                       armed = true;
+               } else
+                       cancel();
+       }
+
+       void fire_more() {
+               if (firing)
+                       send_command("fire");
+       }
+
+       void fire_down() {
+               if (arm.isEnabled() && arm.isSelected() && armed_count > 0) {
+                       firing = true;
+                       fire_more();
+                       fire_timer.restart();
+               }
+       }
+
+       void fire_up() {
+               firing = false;
+               fire_timer.stop();
+       }
+
+       void set_radio() {
+               try {
+                       radio_channel = Integer.parseInt(radio_channel_text.getText());
+               } catch (NumberFormatException ne) {
+                       radio_channel_text.setText(String.format("%d", radio_channel));
+               }
+       }
+
+       void set_serial() {
+               try {
+                       launcher_serial = Integer.parseInt(launcher_serial_text.getText());
+                       AltosUIPreferences.set_launcher_serial(launcher_serial);
+                       send_command("set_remote");
+               } catch (NumberFormatException ne) {
+                       launcher_serial_text.setText(String.format("%d", launcher_serial));
+               }
+       }
+
+       void set_channel() {
+               try {
+                       launcher_channel = Integer.parseInt(launcher_channel_text.getText());
+                       AltosUIPreferences.set_launcher_serial(launcher_channel);
+                       send_command("set_remote");
+               } catch (NumberFormatException ne) {
+                       launcher_channel_text.setText(String.format("%d", launcher_channel));
+               }
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String cmd = e.getActionCommand();
+               if (cmd.equals("armed") || cmd.equals("igniter")) {
+                       stop_arm_timer();
+               }
+
+               if (cmd.equals("arm"))
+                       arm();
+               if (cmd.equals("tick_arm"))
+                       tick_arm_timer();
+               if (cmd.equals("close"))
+                       close();
+               if (cmd.equals("fire_down"))
+                       fire_down();
+               if (cmd.equals("fire_up"))
+                       fire_up();
+               if (cmd.equals("tick_fire"))
+                       fire_more();
+               if (cmd.equals("new_serial"))
+                       set_serial();
+               if (cmd.equals("new_channel"))
+                       set_channel();
+       }
+
+       /* A window listener to catch closing events and tell the config code */
+       class ConfigListener extends WindowAdapter {
+               AltosLaunchUI   ui;
+
+               public ConfigListener(AltosLaunchUI this_ui) {
+                       ui = this_ui;
+               }
+
+               public void windowClosing(WindowEvent e) {
+                       ui.actionPerformed(new ActionEvent(e.getSource(),
+                                                          ActionEvent.ACTION_PERFORMED,
+                                                          "close"));
+               }
+       }
+
+       private boolean open() {
+               command_queue = new LinkedBlockingQueue<String>();
+
+               device = AltosDeviceDialog.show(owner, Altos.product_any);
+               if (device != null) {
+                               LaunchHandler   handler = new LaunchHandler(owner);
+                               Thread          t = new Thread(handler);
+                               t.start();
+                               return true;
+               }
+               return false;
+       }
+
+       public AltosLaunchUI(JFrame in_owner) {
+
+               launcher_channel = AltosUIPreferences.launcher_channel();
+               launcher_serial = AltosUIPreferences.launcher_serial();
+               owner = in_owner;
+               armed_status = AltosLaunch.Unknown;
+               igniter_status = AltosLaunch.Unknown;
+
+               if (!open())
+                       return;
+
+               Container               pane = getContentPane();
+               GridBagConstraints      c = new GridBagConstraints();
+               Insets                  i = new Insets(4,4,4,4);
+
+               arm_timer = new javax.swing.Timer(arm_timeout, this);
+               arm_timer.setActionCommand("tick_arm");
+               arm_timer.restart();
+
+               fire_timer = new javax.swing.Timer(fire_timeout, this);
+               fire_timer.setActionCommand("tick_fire");
+
+               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 ("Launch Controller");
+               pane.add(label, c);
+
+               c.gridx = 0;
+               c.gridy = 1;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_serial_label = new JLabel("Launcher Serial");
+               pane.add(launcher_serial_label, c);
+
+               c.gridx = 1;
+               c.gridy = 1;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_serial_text = new JTextField(7);
+               launcher_serial_text.setText(String.format("%d", launcher_serial));
+               launcher_serial_text.setActionCommand("new_serial");
+               launcher_serial_text.addActionListener(this);
+               pane.add(launcher_serial_text, c);
+
+               c.gridx = 0;
+               c.gridy = 2;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_channel_label = new JLabel("Launcher Channel");
+               pane.add(launcher_channel_label, c);
+
+               c.gridx = 1;
+               c.gridy = 2;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               launcher_channel_text = new JTextField(7);
+               launcher_channel_text.setText(String.format("%d", launcher_channel));
+               launcher_channel_text.setActionCommand("new_channel");
+               launcher_channel_text.addActionListener(this);
+               pane.add(launcher_channel_text, c);
+
+               c.gridx = 0;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               armed_label = new JLabel ("Armed");
+               pane.add(armed_label, c);
+
+               c.gridx = 1;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               armed_status_label = new JLabel();
+               pane.add(armed_status_label, c);
+
+               c.gridx = 0;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               igniter = new JLabel ("Igniter");
+               pane.add(igniter, c);
+
+               c.gridx = 1;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.WEST;
+               igniter_status_label = new JLabel();
+               pane.add(igniter_status_label, c);
+
+               c.gridx = 0;
+               c.gridy = 5;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+               arm = new JToggleButton ("Arm");
+               pane.add(arm, c);
+               arm.addActionListener(this);
+               arm.setActionCommand("arm");
+               arm.setEnabled(true);
+
+               c.gridx = 1;
+               c.gridy = 5;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+               fire = new FireButton ("Fire");
+               fire.setEnabled(false);
+               pane.add(fire, c);
+               fire.addActionListener(this);
+               fire.setActionCommand("fire");
+
+               pack();
+               setLocationRelativeTo(owner);
+
+               addWindowListener(new ConfigListener(this));
+
+               setVisible(true);
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosLed.java b/altosui/AltosLed.java
new file mode 100644 (file)
index 0000000..1358cd4
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+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 (file)
index 0000000..8bd9e7d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+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/altosui/AltosPad.java b/altosui/AltosPad.java
new file mode 100644 (file)
index 0000000..0a3f3d6
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+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 void show() {
+                       label.setVisible(true);
+                       value.setVisible(true);
+                       lights.setVisible(true);
+               }
+
+               public void hide() {
+                       label.setVisible(false);
+                       value.setVisible(false);
+                       lights.setVisible(false);
+               }
+
+               public void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+               }
+
+               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 show() {
+                       label.setVisible(true);
+                       value.setVisible(true);
+               }
+
+               void hide() {
+                       label.setVisible(false);
+                       value.setVisible(false);
+               }
+
+               public void set_font() {
+                       label.setFont(Altos.label_font);
+                       value.setFont(Altos.value_font);
+               }
+
+               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) {
+                       show();
+                       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) {
+                       show();
+                       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 LoggingReady extends LaunchStatus {
+               void show (AltosState state, int crc_errors) {
+                       show();
+                       if (state.data.flight != 0) {
+                               if (state.data.state <= Altos.ao_flight_pad)
+                                       value.setText("Ready to record");
+                               else if (state.data.state < Altos.ao_flight_landed)
+                                       value.setText("Recording data");
+                               else
+                                       value.setText("Recorded data");
+                       }
+                       else
+                               value.setText("Storage full");
+                       lights.set(state.data.flight != 0);
+               }
+               public LoggingReady (GridBagLayout layout, int y) {
+                       super(layout, y, "On-board Data Logging");
+               }
+       }
+
+       LoggingReady logging_ready;
+
+       class GPSLocked extends LaunchStatus {
+               void show (AltosState state, int crc_errors) {
+                       show();
+                       value.setText(String.format("%4d sats", state.gps.nsat));
+                       lights.set(state.gps.locked && state.gps.nsat >= 4);
+               }
+               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) {
+                       show();
+                       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) {
+                       show();
+                       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) {
+                       show();
+                       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();
+               logging_ready.reset();
+               gps_locked.reset();
+               gps_ready.reset();
+               pad_lat.reset();
+               pad_lon.reset();
+               pad_alt.reset();
+       }
+
+       public void set_font() {
+               battery.set_font();
+               apogee.set_font();
+               main.set_font();
+               logging_ready.set_font();
+               gps_locked.set_font();
+               gps_ready.set_font();
+               pad_lat.set_font();
+               pad_lon.set_font();
+               pad_alt.set_font();
+       }
+       
+       public void show(AltosState state, int crc_errors) {
+               battery.show(state, crc_errors);
+               if (state.drogue_sense == AltosRecord.MISSING)
+                       apogee.hide();
+               else
+                       apogee.show(state, crc_errors);
+               if (state.main_sense == AltosRecord.MISSING)
+                       main.hide();
+               else
+                       main.show(state, crc_errors);
+               logging_ready.show(state, crc_errors);
+               pad_alt.show(state, crc_errors);
+               if (state.gps != null && state.gps.connected) {
+                       gps_locked.show(state, crc_errors);
+                       gps_ready.show(state, crc_errors);
+                       pad_lat.show(state, crc_errors);
+                       pad_lon.show(state, crc_errors);
+               } else {
+                       gps_locked.hide();
+                       gps_ready.hide();
+                       pad_lat.hide();
+                       pad_lon.hide();
+               }
+       }
+
+       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);
+               logging_ready = new LoggingReady(layout, 3);
+               gps_locked = new GPSLocked(layout, 4);
+               gps_ready = new GPSReady(layout, 5);
+               pad_lat = new PadLat(layout, 6);
+               pad_lon = new PadLon(layout, 7);
+               pad_alt = new PadAlt(layout, 8);
+       }
+}
diff --git a/altosui/AltosRomconfig.java b/altosui/AltosRomconfig.java
new file mode 100644 (file)
index 0000000..0a283e5
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosRomconfig {
+       public boolean  valid;
+       public int      version;
+       public int      check;
+       public int      serial_number;
+       public int      radio_calibration;
+
+       static int get_int(byte[] bytes, int start, int len) {
+               int     v = 0;
+               int     o = 0;
+               while (len > 0) {
+                       v = v | ((((int) bytes[start]) & 0xff) << o);
+                       start++;
+                       len--;
+                       o += 8;
+               }
+               return v;
+       }
+
+       static void put_int(int value, byte[] bytes, int start, int len) {
+               while (len > 0) {
+                       bytes[start] = (byte) (value & 0xff);
+                       start++;
+                       len--;
+                       value >>= 8;
+               }
+       }
+
+       static void put_string(String value, byte[] bytes, int start) {
+               for (int i = 0; i < value.length(); i++)
+                       bytes[start + i] = (byte) value.charAt(i);
+       }
+
+       static final int AO_USB_DESC_STRING     = 3;
+
+       static void put_usb_serial(int value, byte[] bytes, int start) {
+               int offset = start + 0xa;
+               int string_num = 0;
+
+               while (offset < bytes.length && bytes[offset] != 0) {
+                       if (bytes[offset + 1] == AO_USB_DESC_STRING) {
+                               ++string_num;
+                               if (string_num == 4)
+                                       break;
+                       }
+                       offset += ((int) bytes[offset]) & 0xff;
+               }
+               if (offset >= bytes.length || bytes[offset] == 0)
+                       return;
+               int len = ((((int) bytes[offset]) & 0xff) - 2) / 2;
+               String fmt = String.format("%%0%dd", len);
+
+               String s = String.format(fmt, value);
+               if (s.length() != len) {
+                       System.out.printf("weird usb length issue %s isn't %d\n",
+                                         s, len);
+                       return;
+               }
+               for (int i = 0; i < len; i++) {
+                       bytes[offset + 2 + i*2] = (byte) s.charAt(i);
+                       bytes[offset + 2 + i*2+1] = 0;
+               }
+       }
+
+       public AltosRomconfig(byte[] bytes, int offset) {
+               version = get_int(bytes, offset + 0, 2);
+               check = get_int(bytes, offset + 2, 2);
+               if (check == (~version & 0xffff)) {
+                       switch (version) {
+                       case 2:
+                       case 1:
+                               serial_number = get_int(bytes, offset + 4, 2);
+                               radio_calibration = get_int(bytes, offset + 6, 4);
+                               valid = true;
+                               break;
+                       }
+               }
+       }
+
+       public AltosRomconfig(AltosHexfile hexfile) {
+               this(hexfile.data, 0xa0 - hexfile.address);
+       }
+
+       public void write(byte[] bytes, int offset) throws IOException {
+               if (!valid)
+                       throw new IOException("rom configuration invalid");
+
+               if (offset < 0 || bytes.length < offset + 10)
+                       throw new IOException("image cannot contain rom config");
+
+               AltosRomconfig existing = new AltosRomconfig(bytes, offset);
+               if (!existing.valid)
+                       throw new IOException("image does not contain existing rom config");
+
+               switch (existing.version) {
+               case 2:
+                       put_usb_serial(serial_number, bytes, offset);
+               case 1:
+                       put_int(serial_number, bytes, offset + 4, 2);
+                       put_int(radio_calibration, bytes, offset + 6, 4);
+                       break;
+               }
+       }
+
+       public void write (AltosHexfile hexfile) throws IOException {
+               write(hexfile.data, 0xa0 - hexfile.address);
+               AltosRomconfig check = new AltosRomconfig(hexfile);
+               if (!check.valid())
+                       throw new IOException("writing new rom config failed\n");
+       }
+
+       public AltosRomconfig(int in_serial_number, int in_radio_calibration) {
+               valid = true;
+               version = 1;
+               check = (~version & 0xffff);
+               serial_number = in_serial_number;
+               radio_calibration = in_radio_calibration;
+       }
+
+       public boolean valid() {
+               return valid && serial_number != 0;
+       }
+
+       public AltosRomconfig() {
+               valid = false;
+       }
+}
diff --git a/altosui/AltosRomconfigUI.java b/altosui/AltosRomconfigUI.java
new file mode 100644 (file)
index 0000000..306b862
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+public class AltosRomconfigUI
+       extends AltosDialog
+       implements ActionListener
+{
+       Container       pane;
+       Box             box;
+       JLabel          serial_label;
+       JLabel          radio_calibration_label;
+
+       JFrame          owner;
+       JTextField      serial_value;
+       JTextField      radio_calibration_value;
+
+       JButton         ok;
+       JButton         cancel;
+
+       /* Build the UI using a grid bag */
+       public AltosRomconfigUI(JFrame in_owner) {
+               super (in_owner, "Configure TeleMetrum Rom Values", true);
+
+               owner = in_owner;
+               GridBagConstraints c;
+
+               Insets il = new Insets(4,4,4,4);
+               Insets ir = new Insets(4,4,4,4);
+
+               pane = getContentPane();
+               pane.setLayout(new GridBagLayout());
+
+               /* Serial */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 0;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               serial_label = new JLabel("Serial:");
+               pane.add(serial_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 3; c.gridy = 0;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               serial_value = new JTextField("0");
+               pane.add(serial_value, c);
+
+               /* Radio calibration value */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 1;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = il;
+               c.ipady = 5;
+               radio_calibration_label = new JLabel("Radio Calibration:");
+               pane.add(radio_calibration_label, c);
+
+               c = new GridBagConstraints();
+               c.gridx = 3; c.gridy = 1;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.weightx = 1;
+               c.anchor = GridBagConstraints.LINE_START;
+               c.insets = ir;
+               c.ipady = 5;
+               radio_calibration_value = new JTextField("1186611");
+               pane.add(radio_calibration_value, c);
+
+               /* Buttons */
+               c = new GridBagConstraints();
+               c.gridx = 0; c.gridy = 2;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = il;
+               ok = new JButton("OK");
+               pane.add(ok, c);
+               ok.addActionListener(this);
+               ok.setActionCommand("ok");
+
+               c = new GridBagConstraints();
+               c.gridx = 3; c.gridy = 2;
+               c.gridwidth = 3;
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = il;
+               cancel = new JButton("Cancel");
+               pane.add(cancel, c);
+               cancel.addActionListener(this);
+               cancel.setActionCommand("cancel");
+
+               pack();
+               setLocationRelativeTo(owner);
+       }
+
+       public AltosRomconfigUI(JFrame frame, AltosRomconfig config) {
+               this(frame);
+               set(config);
+       }
+
+       boolean selected;
+
+       /* Listen for events from our buttons */
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("ok")) {
+                       AltosRomconfig  romconfig = romconfig();
+                       if (romconfig == null || !romconfig.valid()) {
+                               JOptionPane.showMessageDialog(this,
+                                                             "Invalid serial number or radio calibration value",
+                                                             "Invalid rom configuration",
+                                                             JOptionPane.ERROR_MESSAGE);
+                               return;
+                       }
+                       selected = true;
+               }
+               setVisible(false);
+       }
+
+       int serial() {
+               return Integer.parseInt(serial_value.getText());
+       }
+
+       void set_serial(int serial) {
+               serial_value.setText(String.format("%d", serial));
+       }
+
+       int radio_calibration() {
+               return Integer.parseInt(radio_calibration_value.getText());
+       }
+
+       void set_radio_calibration(int calibration) {
+               radio_calibration_value.setText(String.format("%d", calibration));
+       }
+
+       public void set(AltosRomconfig config) {
+               if (config != null && config.valid()) {
+                       set_serial(config.serial_number);
+                       set_radio_calibration(config.radio_calibration);
+               }
+       }
+
+       AltosRomconfig romconfig() {
+               try {
+                       return new AltosRomconfig(serial(), radio_calibration());
+               } catch (NumberFormatException ne) {
+                       return null;
+               }
+       }
+
+       public AltosRomconfig showDialog() {
+               setVisible(true);
+               if (selected)
+                       return romconfig();
+               return null;
+       }
+
+       public static AltosRomconfig show(JFrame frame, AltosRomconfig config) {
+               AltosRomconfigUI ui = new AltosRomconfigUI(frame, config);
+               return ui.showDialog();
+       }
+}
diff --git a/altosui/AltosScanUI.java b/altosui/AltosScanUI.java
new file mode 100644 (file)
index 0000000..9da1290
--- /dev/null
@@ -0,0 +1,553 @@
+/*
+ * Copyright © 2011 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.*;
+import org.altusmetrum.AltosLib.*;
+
+class AltosScanResult {
+       String          callsign;
+       int             serial;
+       int             flight;
+       AltosFrequency  frequency;
+       int             telemetry;
+       
+       boolean interrupted = false;
+       
+       public String toString() {
+               return String.format("%-9.9s serial %-4d flight %-4d (%s %s)",
+                                    callsign, serial, flight, frequency.toShortString(), Altos.telemetry_name(telemetry));
+       }
+
+       public String toShortString() {
+               return String.format("%s %d %d %7.3f %d",
+                                    callsign, serial, flight, frequency, telemetry);
+       }
+
+       public AltosScanResult(String in_callsign, int in_serial,
+                              int in_flight, AltosFrequency in_frequency, int in_telemetry) {
+               callsign = in_callsign;
+               serial = in_serial;
+               flight = in_flight;
+               frequency = in_frequency;
+               telemetry = in_telemetry;
+       }
+
+       public boolean equals(AltosScanResult other) {
+               return (serial == other.serial &&
+                       frequency.frequency == other.frequency.frequency &&
+                       telemetry == other.telemetry);
+       }
+
+       public boolean up_to_date(AltosScanResult other) {
+               if (flight == 0 && other.flight != 0) {
+                       flight = other.flight;
+                       return false;
+               }
+               if (callsign.equals("N0CALL") && !other.callsign.equals("N0CALL")) {
+                       callsign = other.callsign;
+                       return false;
+               }
+               return true;
+       }
+}
+
+class AltosScanResults extends LinkedList<AltosScanResult> implements ListModel {
+       
+       LinkedList<ListDataListener>    listeners = new LinkedList<ListDataListener>();
+
+       void changed(ListDataEvent de) {
+               for (ListDataListener l : listeners)
+                       l.contentsChanged(de);
+       }
+
+       public boolean add(AltosScanResult r) {
+               int i = 0;
+               for (AltosScanResult old : this) {
+                       if (old.equals(r)) {
+                               if (!old.up_to_date(r))
+                                       changed (new ListDataEvent(this,
+                                                                  ListDataEvent.CONTENTS_CHANGED,
+                                                                  i, i));
+                               return true;
+                       }
+                       i++;
+               }
+
+               super.add(r);
+               changed(new ListDataEvent(this,
+                                         ListDataEvent.INTERVAL_ADDED,
+                                         this.size() - 2, this.size() - 1));
+               return true;
+       }
+
+       public void addListDataListener(ListDataListener l) {
+               listeners.add(l);
+       }
+       
+       public void removeListDataListener(ListDataListener l) {
+               listeners.remove(l);
+       }
+
+       public AltosScanResult getElementAt(int i) {
+               return this.get(i);
+       }
+
+       public int getSize() {
+               return this.size();
+       }
+}
+
+public class AltosScanUI
+       extends AltosDialog
+       implements ActionListener
+{
+       AltosUI                         owner;
+       AltosDevice                     device;
+       AltosConfigData                 config_data;
+       AltosTelemetryReader            reader;
+       private JList                   list;
+       private JLabel                  scanning_label;
+       private JLabel                  frequency_label;
+       private JLabel                  telemetry_label;
+       private JButton                 cancel_button;
+       private JButton                 monitor_button;
+       private JCheckBox[]             telemetry_boxes;
+       javax.swing.Timer               timer;
+       AltosScanResults                results = new AltosScanResults();
+
+       int                             telemetry;
+
+       final static int                timeout = 1200;
+       TelemetryHandler                handler;
+       Thread                          thread;
+       AltosFrequency[]                frequencies;
+       int                             frequency_index;
+
+       void scan_exception(Exception e) {
+               if (e instanceof FileNotFoundException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     ((FileNotFoundException) e).getMessage(),
+                                                     "Cannot open target device",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof AltosSerialInUseException) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Device \"%s\" already in use",
+                                                                   device.toShortString()),
+                                                     "Device in use",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else if (e instanceof IOException) {
+                       IOException ee = (IOException) e;
+                       JOptionPane.showMessageDialog(owner,
+                                                     device.toShortString(),
+                                                     ee.getLocalizedMessage(),
+                                                     JOptionPane.ERROR_MESSAGE);
+               } else {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Connection to \"%s\" failed",
+                                                                   device.toShortString()),
+                                                     "Connection Failed",
+                                                     JOptionPane.ERROR_MESSAGE);
+               }
+               close();
+       }
+
+       class TelemetryHandler implements Runnable {
+
+               public void run() {
+
+                       boolean interrupted = false;
+
+                       try {
+                               for (;;) {
+                                       try {
+                                               AltosRecord     record = reader.read();
+                                               if (record == null)
+                                                       continue;
+                                               if ((record.seen & AltosRecord.seen_flight) != 0) {
+                                                       final AltosScanResult   result = new AltosScanResult(record.callsign,
+                                                                                                    record.serial,
+                                                                                                    record.flight,
+                                                                                                    frequencies[frequency_index],
+                                                                                                    telemetry);
+                                                       Runnable r = new Runnable() {
+                                                                       public void run() {
+                                                                               results.add(result);
+                                                                       }
+                                                               };
+                                                       SwingUtilities.invokeLater(r);
+                                               }
+                                       } catch (ParseException pp) {
+                                       } catch (AltosCRCException ce) {
+                                       }
+                               }
+                       } catch (InterruptedException ee) {
+                               interrupted = true;
+                       } catch (IOException ie) {
+                       } finally {
+                               reader.close(interrupted);
+                       }
+               }
+       }
+
+       void set_label() {
+               frequency_label.setText(String.format("Frequency: %s", frequencies[frequency_index].toString()));
+               telemetry_label.setText(String.format("Telemetry: %s", Altos.telemetry_name(telemetry)));
+       }
+
+       void set_telemetry() {
+               reader.set_telemetry(telemetry);
+       }
+       
+       void set_frequency() throws InterruptedException, TimeoutException {
+               reader.set_frequency(frequencies[frequency_index].frequency);
+               reader.reset();
+       }
+       
+       void next() throws InterruptedException, TimeoutException {
+               reader.set_monitor(false);
+               Thread.sleep(100);
+               ++frequency_index;
+               if (frequency_index >= frequencies.length ||
+                   !telemetry_boxes[telemetry - Altos.ao_telemetry_min].isSelected())
+               {
+                       frequency_index = 0;
+                       do {
+                               ++telemetry;
+                               if (telemetry > Altos.ao_telemetry_max)
+                                       telemetry = Altos.ao_telemetry_min;
+                       } while (!telemetry_boxes[telemetry - Altos.ao_telemetry_min].isSelected());
+                       set_telemetry();
+               }
+               set_frequency();
+               set_label();
+               reader.set_monitor(true);
+       }
+
+
+       void close() {
+               if (thread != null && thread.isAlive()) {
+                       thread.interrupt();
+                       try {
+                               thread.join();
+                       } catch (InterruptedException ie) {}
+               }
+               thread = null;
+               if (timer != null)
+                       timer.stop();
+               setVisible(false);
+               dispose();
+       }
+
+       void tick_timer() throws InterruptedException, TimeoutException {
+               next();
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String cmd = e.getActionCommand();
+
+               try {
+                       if (cmd.equals("cancel"))
+                               close();
+
+                       if (cmd.equals("tick"))
+                               tick_timer();
+
+                       if (cmd.equals("telemetry")) {
+                               int k;
+                               int scanning_telemetry = 0;
+                               for (k = Altos.ao_telemetry_min; k <= Altos.ao_telemetry_max; k++) {
+                                       int j = k - Altos.ao_telemetry_min;
+                                       if (telemetry_boxes[j].isSelected())
+                                               scanning_telemetry |= (1 << k);
+                               }
+                               if (scanning_telemetry == 0) {
+                                       scanning_telemetry |= (1 << Altos.ao_telemetry_standard);
+                                       telemetry_boxes[Altos.ao_telemetry_standard - Altos.ao_telemetry_min].setSelected(true);
+                               }
+                               AltosUIPreferences.set_scanning_telemetry(scanning_telemetry);
+                       }
+
+                       if (cmd.equals("monitor")) {
+                               close();
+                               AltosScanResult r = (AltosScanResult) (list.getSelectedValue());
+                               if (r != null) {
+                                       if (device != null) {
+                                               if (reader != null) {
+                                                       reader.set_telemetry(r.telemetry);
+                                                       reader.set_frequency(r.frequency.frequency);
+                                                       reader.save_frequency();
+                                                       owner.telemetry_window(device);
+                                               }
+                                       }
+                               }
+                       }
+               } catch (TimeoutException te) {
+                       close();
+               } catch (InterruptedException ie) {
+                       close();
+               }
+       }
+
+       /* A window listener to catch closing events and tell the config code */
+       class ConfigListener extends WindowAdapter {
+               AltosScanUI     ui;
+
+               public ConfigListener(AltosScanUI 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, Altos.product_basestation);
+               if (device == null)
+                       return false;
+               try {
+                       reader = new AltosTelemetryReader(new AltosSerial(device));
+                       set_frequency();
+                       set_telemetry();
+                       try {
+                               Thread.sleep(100);
+                       } catch (InterruptedException ie) {
+                       }
+                       reader.flush();
+                       handler = new TelemetryHandler();
+                       thread = new Thread(handler);
+                       thread.start();
+                       return true;
+               } catch (FileNotFoundException ee) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     ee.getMessage(),
+                                                     "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(),
+                                                     "Unkonwn I/O error",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } catch (TimeoutException te) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     device.toShortString(),
+                                                     "Timeout error",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } catch (InterruptedException ie) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     device.toShortString(),
+                                                     "Interrupted exception",
+                                                     JOptionPane.ERROR_MESSAGE);
+               }
+               if (reader != null)
+                       reader.close(false);
+               return false;
+       }
+
+       public AltosScanUI(AltosUI in_owner) {
+
+               owner = in_owner;
+
+               frequencies = AltosUIPreferences.common_frequencies();
+               frequency_index = 0;
+               telemetry = Altos.ao_telemetry_min;
+
+               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.restart();
+
+               owner = in_owner;
+
+               pane.setLayout(new GridBagLayout());
+
+               scanning_label = new JLabel("Scanning:");
+               frequency_label = new JLabel("");
+               telemetry_label = new JLabel("");
+               
+               set_label();
+
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.WEST;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 1;
+
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 2;
+
+               pane.add(scanning_label, c);
+               c.gridy = 1;
+               pane.add(frequency_label, c);
+               c.gridy = 2;
+               pane.add(telemetry_label, c);
+
+               int     scanning_telemetry = AltosUIPreferences.scanning_telemetry();
+               telemetry_boxes = new JCheckBox[Altos.ao_telemetry_max - Altos.ao_telemetry_min + 1];
+               for (int k = Altos.ao_telemetry_min; k <= Altos.ao_telemetry_max; k++) {
+                       int j = k - Altos.ao_telemetry_min;
+                       telemetry_boxes[j] = new JCheckBox(AltosLib.telemetry_name(k));
+                       c.gridy = 3 + j;
+                       pane.add(telemetry_boxes[j], c);
+                       telemetry_boxes[j].setActionCommand("telemetry");
+                       telemetry_boxes[j].addActionListener(this);
+                       telemetry_boxes[j].setSelected((scanning_telemetry & (1 << k)) != 0);
+               }
+
+               int     y_offset = 3 + (Altos.ao_telemetry_max - Altos.ao_telemetry_min + 1);
+                               
+               list = new JList(results) {
+                               //Subclass JList to workaround bug 4832765, which can cause the
+                               //scroll pane to not let the user easily scroll up to the beginning
+                               //of the list.  An alternative would be to set the unitIncrement
+                               //of the JScrollBar to a fixed value. You wouldn't get the nice
+                               //aligned scrolling, but it should work.
+                               public int getScrollableUnitIncrement(Rectangle visibleRect,
+                                                                     int orientation,
+                                                                     int direction) {
+                                       int row;
+                                       if (orientation == SwingConstants.VERTICAL &&
+                                           direction < 0 && (row = getFirstVisibleIndex()) != -1) {
+                                               Rectangle r = getCellBounds(row, row);
+                                               if ((r.y == visibleRect.y) && (row != 0))  {
+                                                       Point loc = r.getLocation();
+                                                       loc.y--;
+                                                       int prevIndex = locationToIndex(loc);
+                                                       Rectangle prevR = getCellBounds(prevIndex, prevIndex);
+
+                                                       if (prevR == null || prevR.y >= r.y) {
+                                                               return 0;
+                                                       }
+                                                       return prevR.height;
+                                               }
+                                       }
+                                       return super.getScrollableUnitIncrement(
+                                               visibleRect, orientation, direction);
+                               }
+                       };
+
+               list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+               list.setLayoutOrientation(JList.HORIZONTAL_WRAP);
+               list.setVisibleRowCount(-1);
+
+               list.addMouseListener(new MouseAdapter() {
+                                public void mouseClicked(MouseEvent e) {
+                                        if (e.getClickCount() == 2) {
+                                                monitor_button.doClick(); //emulate button click
+                                        }
+                                }
+                       });
+               JScrollPane listScroller = new JScrollPane(list);
+               listScroller.setPreferredSize(new Dimension(400, 80));
+               listScroller.setAlignmentX(LEFT_ALIGNMENT);
+
+               //Create a container so that we can add a title around
+               //the scroll pane.  Can't add a title directly to the
+               //scroll pane because its background would be white.
+               //Lay out the label and scroll pane from top to bottom.
+               JPanel listPane = new JPanel();
+               listPane.setLayout(new BoxLayout(listPane, BoxLayout.PAGE_AXIS));
+
+               JLabel label = new JLabel("Select Device");
+               label.setLabelFor(list);
+               listPane.add(label);
+               listPane.add(Box.createRigidArea(new Dimension(0,5)));
+               listPane.add(listScroller);
+               listPane.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
+
+               c.fill = GridBagConstraints.BOTH;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 1;
+
+               c.gridx = 0;
+               c.gridy = y_offset;
+               c.gridwidth = 2;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(listPane, c);
+
+               cancel_button = new JButton("Cancel");
+               cancel_button.addActionListener(this);
+               cancel_button.setActionCommand("cancel");
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 1;
+
+               c.gridx = 0;
+               c.gridy = y_offset + 1;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(cancel_button, c);
+
+               monitor_button = new JButton("Monitor");
+               monitor_button.addActionListener(this);
+               monitor_button.setActionCommand("monitor");
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 1;
+
+               c.gridx = 1;
+               c.gridy = y_offset + 1;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(monitor_button, c);
+
+               pack();
+               setLocationRelativeTo(owner);
+
+               addWindowListener(new ConfigListener(this));
+
+               setVisible(true);
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosSerial.java b/altosui/AltosSerial.java
new file mode 100644 (file)
index 0000000..6cee160
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+/*
+ * Deal with TeleDongle on a serial port
+ */
+
+package altosui;
+
+import java.lang.*;
+import java.io.*;
+import java.util.concurrent.*;
+import java.util.*;
+import java.text.*;
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import javax.swing.filechooser.FileNameExtensionFilter;
+import javax.swing.table.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+/*
+ * This class reads from the serial port and places each received
+ * line in a queue. Dealing with that queue is left up to other
+ * threads.
+ */
+
+public class AltosSerial extends AltosLink  {
+
+       static java.util.List<String> devices_opened = Collections.synchronizedList(new LinkedList<String>());
+
+       AltosDevice device;
+       SWIGTYPE_p_altos_file altos;
+       Thread input_thread;
+       String line;
+       byte[] line_bytes;
+       int line_count;
+       Frame frame;
+
+       public int getchar() {
+               if (altos == null)
+                       return ERROR;
+               return libaltos.altos_getchar(altos, 0);
+       }
+
+       public void flush_output() {
+               super.flush_output();
+               if (altos != null) {
+                       if (libaltos.altos_flush(altos) != 0) {
+                               libaltos.altos_close(altos);
+                               altos = null;
+                               abort_reply();
+                       }
+               }
+       }
+
+       JDialog         timeout_dialog;
+
+       private void start_timeout_dialog_internal() {
+
+               Object[] options = { "Cancel" };
+
+               JOptionPane     pane = new JOptionPane();
+               pane.setMessage(String.format("Connecting to %s, %7.3f MHz", device.toShortString(), frequency));
+               pane.setOptions(options);
+               pane.setInitialValue(null);
+
+               timeout_dialog = pane.createDialog(frame, "Connecting...");
+
+               timeout_dialog.setVisible(true);
+
+               Object o = pane.getValue();
+               if (o == null)
+                       return;
+               if (options[0].equals(o))
+                       reply_abort = true;
+               timeout_dialog.dispose();
+               timeout_dialog = null;
+       }
+
+       /*
+        * These are required by the AltosLink implementation
+        */
+
+       public boolean can_cancel_reply() {
+               /*
+                * Can cancel any replies not called from the dispatch thread
+                */
+               return !SwingUtilities.isEventDispatchThread();
+       }
+
+       public boolean show_reply_timeout() {
+               if (!SwingUtilities.isEventDispatchThread() && frame != null) {
+                       Runnable r = new Runnable() {
+                                       public void run() {
+                                               start_timeout_dialog_internal();
+                                       }
+                               };
+                       SwingUtilities.invokeLater(r);
+                       return true;
+               }
+               return false;
+       }
+
+       public void hide_reply_timeout() {
+               Runnable r = new Runnable() {
+                               public void run() {
+                                       timeout_dialog.setVisible(false);
+                               }
+                       };
+               SwingUtilities.invokeLater(r);
+       }
+
+       public void close() {
+               if (remote) {
+                       try {
+                               stop_remote();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+               if (in_reply != 0)
+                       System.out.printf("Uh-oh. Closing active serial device\n");
+
+               if (altos != null) {
+                       libaltos.altos_close(altos);
+               }
+               if (input_thread != null) {
+                       try {
+                               input_thread.interrupt();
+                               input_thread.join();
+                       } catch (InterruptedException e) {
+                       }
+                       input_thread = null;
+               }
+               if (altos != null) {
+                       libaltos.altos_free(altos);
+                       altos = null;
+               }
+               synchronized (devices_opened) {
+                       devices_opened.remove(device.getPath());
+               }
+               if (debug)
+                       System.out.printf("Closing %s\n", device.getPath());
+       }
+
+       private void putc(char c) {
+               if (altos != null)
+                       if (libaltos.altos_putchar(altos, c) != 0) {
+                               libaltos.altos_close(altos);
+                               altos = null;
+                               abort_reply();
+                       }
+       }
+
+       public void print(String data) {
+               for (int i = 0; i < data.length(); i++)
+                       putc(data.charAt(i));
+       }
+
+       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 = device.open();
+               if (altos == null) {
+                       final String    message = device.getErrorString();
+                       close();
+                       throw new FileNotFoundException(String.format("%s (%s)",
+                                                                     device.toShortString(), message));
+               }
+               if (debug)
+                       System.out.printf("Open %s\n", device.getPath());
+               input_thread = new Thread(this);
+               input_thread.start();
+               print("~\nE 0\n");
+               set_monitor(false);
+               flush_output();
+       }
+
+       public void set_frame(Frame in_frame) {
+               frame = in_frame;
+       }
+
+       public AltosSerial(AltosDevice in_device) throws FileNotFoundException, AltosSerialInUseException {
+               device = in_device;
+               frame = null;
+               serial = device.getSerial();
+               name = device.toShortString();
+               open();
+       }
+}
diff --git a/altosui/AltosSerialInUseException.java b/altosui/AltosSerialInUseException.java
new file mode 100644 (file)
index 0000000..7380f33
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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 class AltosSerialInUseException extends Exception {
+       public AltosDevice      device;
+
+       public AltosSerialInUseException (AltosDevice in_device) {
+               device = in_device;
+       }
+}
diff --git a/altosui/AltosSiteMap.java b/altosui/AltosSiteMap.java
new file mode 100644 (file)
index 0000000..b57edca
--- /dev/null
@@ -0,0 +1,447 @@
+/*
+ * 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;
+import java.util.concurrent.*;
+import org.altusmetrum.AltosLib.*;
+
+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;
+
+       int radius;     /* half width/height of tiles to load */
+
+       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);
+       }
+
+       ConcurrentHashMap<Point,AltosSiteMapTile> mapTiles = new ConcurrentHashMap<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
+       }
+
+       public void set_font() {
+               // nothing
+       }
+
+       private void loadMap(final AltosSiteMapTile tile,
+                            File pngfile, String pngurl)
+       {
+               final ImageIcon res = AltosSiteMapCache.fetchAndLoadMap(pngfile, pngurl);
+               if (res != null) {
+                       SwingUtilities.invokeLater(new Runnable() {
+                                       public void run() {
+                                               tile.loadMap(res);
+                                       }
+                               });
+               } else {
+                       System.out.printf("# Failed to fetch file %s\n", pngfile);
+                       System.out.printf(" wget -O '%s' '%s'\n", pngfile, pngurl);
+               }
+       }
+
+       File pngfile;
+       String pngurl;
+
+       public int prefetchMap(int x, int y) {
+               LatLng map_latlng = latlng(
+                       -centre.x + x*px_size + px_size/2,
+                       -centre.y + y*px_size + px_size/2);
+               pngfile = MapFile(map_latlng.lat, map_latlng.lng, zoom);
+               pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
+               if (pngfile.exists()) {
+                       return 1;
+               } else if (AltosSiteMapCache.fetchMap(pngfile, pngurl)) {
+                       return 0;
+               } else {
+                       return -1;
+               }
+       }
+
+       public static void prefetchMaps(double lat, double lng, int w, int h) {
+               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++) {
+                               int r = asm.prefetchMap(x, y);
+                               switch (r) {
+                               case 1:
+                                       System.out.printf("Already have %s\n", asm.pngfile);
+                                       break;
+                               case 0:
+                                       System.out.printf("Fetched map %s\n", asm.pngfile);
+                                       break;
+                               case -1:
+                                       System.out.printf("# Failed to fetch file %s\n", asm.pngfile);
+                                       System.out.printf(" wget -O '%s' ''\n", asm.pngfile, asm.pngurl);
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       public String initMap(Point offset) {
+               AltosSiteMapTile tile = mapTiles.get(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, zoom);
+               String pngurl = MapURL(map_latlng.lat, map_latlng.lng, zoom);
+               loadMap(tile, pngfile, pngurl);
+               return pngfile.toString();
+       }
+
+       public void setBaseLocation(double lat, double lng) {
+               for (Point k : mapTiles.keySet()) {
+                       AltosSiteMapTile tile = mapTiles.get(k);
+                       tile.clearMap();
+               }
+                       
+               centre = getBaseLocation(lat, lng);
+               scrollRocketToVisible(pt(lat,lng));
+       }
+
+       private void initMaps(double lat, double lng) {
+               setBaseLocation(lat, lng);
+
+               Thread thread = new Thread() {
+                               public void run() {
+                                       for (Point k : mapTiles.keySet())
+                                               initMap(k);
+                               }
+                       };
+               thread.start();
+       }
+
+       private static File MapFile(double lat, double lng, int zoom) {
+               char chlat = lat < 0 ? 'S' : 'N';
+               char chlng = lng < 0 ? 'W' : 'E';
+               if (lat < 0) lat = -lat;
+               if (lng < 0) lng = -lng;
+               return new File(AltosUIPreferences.mapdir(),
+                               String.format("map-%c%.6f,%c%.6f-%d.png",
+                                             chlat, lat, chlng, lng, zoom));
+       }
+
+       private static String MapURL(double lat, double lng, int zoom) {
+               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(double lat, double lon) {
+               initMaps(lat, lon);
+               scrollRocketToVisible(pt(lat, lon));
+       }
+       public void show(final AltosState state, final int crc_errors) {
+               // if insufficient gps data, nothing to update
+               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(offset);
+                       finishTileLater(tile, offset);
+               }
+
+               scrollRocketToVisible(pt);
+
+               if (offset != tileOffset(last_pt)) {
+                       ensureTilesAround(offset);
+               }
+
+               last_pt = pt;
+               last_state = state.state;
+       }
+
+       public void draw_circle(double lat, double lon) {
+               final Point2D.Double pt = pt(lat, lon);
+
+               for (Point offset : mapTiles.keySet()) {
+                       AltosSiteMapTile tile = mapTiles.get(offset);
+                       Point2D.Double ref = translatePoint(pt, tileCoordOffset(offset));
+                       tile.draw_circle(ref);
+               }
+       }
+
+       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 = -radius; x <= radius; x++) {
+                       for (int y = -radius; y <= radius; y++) {
+                               Point offset = new Point(base_offset.x + x, base_offset.y + y);
+                               if (mapTiles.containsKey(offset))
+                                       continue;
+                               AltosSiteMapTile tile = createTile(offset);
+                               initMap(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(int in_radius) {
+               radius = in_radius;
+
+               GrabNDrag scroller = new GrabNDrag(comp);
+
+               comp.setLayout(layout);
+
+               for (int x = -radius; x <= radius; x++) {
+                       for (int y = -radius; y <= radius; y++) {
+                               Point offset = new Point(x, y);
+                               AltosSiteMapTile t = createTile(offset);
+                               addTileAt(t, offset);
+                       }
+               }
+               setViewportView(comp);
+               setPreferredSize(new Dimension(500,500));
+       }
+
+       public AltosSiteMap() {
+               this(1);
+       }
+}
diff --git a/altosui/AltosSiteMapCache.java b/altosui/AltosSiteMapCache.java
new file mode 100644 (file)
index 0000000..f729a29
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * 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;
+import org.altusmetrum.AltosLib.*;
+
+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/AltosSiteMapPreload.java b/altosui/AltosSiteMapPreload.java
new file mode 100644 (file)
index 0000000..676b079
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * Copyright © 2011 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.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;
+import java.net.URL;
+import java.net.URLConnection;
+import org.altusmetrum.AltosLib.*;
+
+class AltosMapPos extends Box {
+       AltosUI         owner;
+       JLabel          label;
+       JComboBox       hemi;
+       JTextField      deg;
+       JLabel          deg_label;
+       JTextField      min;
+       JLabel          min_label;
+
+       public void set_value(double new_value) {
+               double  d, m;
+               int     h;
+
+               h = 0;
+               if (new_value < 0) {
+                       h = 1;
+                       new_value = -new_value;
+               }
+               d = Math.floor(new_value);
+               deg.setText(String.format("%3.0f", d));
+               m = (new_value - d) * 60.0;
+               min.setText(String.format("%7.4f", m));
+               hemi.setSelectedIndex(h);
+       }
+
+       public double get_value() throws NumberFormatException {
+               int     h = hemi.getSelectedIndex();
+               String  d_t = deg.getText();
+               String  m_t = min.getText();
+               double  d, m, v;
+               try {
+                       d = Double.parseDouble(d_t);
+               } catch (NumberFormatException ne) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid degrees \"%s\"",
+                                                                   d_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw ne;
+               }
+               try {
+                       if (m_t.equals(""))
+                               m = 0;
+                       else
+                               m = Double.parseDouble(m_t);
+               } catch (NumberFormatException ne) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid minutes \"%s\"",
+                                                                   m_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw ne;
+               }
+               v = d + m/60.0;
+               if (h == 1)
+                       v = -v;
+               return v;
+       }
+
+       public AltosMapPos(AltosUI in_owner,
+                          String label_value,
+                          String[] hemi_names,
+                          double default_value) {
+               super(BoxLayout.X_AXIS);
+               owner = in_owner;
+               label = new JLabel(label_value);
+               hemi = new JComboBox(hemi_names);
+               hemi.setEditable(false);
+               deg = new JTextField(5);
+               deg.setMinimumSize(deg.getPreferredSize());
+               deg.setHorizontalAlignment(JTextField.RIGHT);
+               deg_label = new JLabel("°");
+               min = new JTextField(9);
+               min.setMinimumSize(min.getPreferredSize());
+               min_label = new JLabel("'");
+               set_value(default_value);
+               add(label);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(hemi);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(deg);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(deg_label);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(min);
+               add(Box.createRigidArea(new Dimension(5, 0)));
+               add(min_label);
+       }
+}
+
+class AltosSite {
+       String  name;
+       double  latitude;
+       double  longitude;
+
+       public String toString() {
+               return name;
+       }
+
+       public AltosSite(String in_name, double in_latitude, double in_longitude) {
+               name = in_name;
+               latitude = in_latitude;
+               longitude = in_longitude;
+       }
+
+       public AltosSite(String line) throws ParseException {
+               String[]        elements = line.split(":");
+
+               if (elements.length < 3)
+                       throw new ParseException(String.format("Invalid site line %s", line), 0);
+
+               name = elements[0];
+
+               try {
+                       latitude = Double.parseDouble(elements[1]);
+                       longitude = Double.parseDouble(elements[2]);
+               } catch (NumberFormatException ne) {
+                       throw new ParseException(String.format("Invalid site line %s", line), 0);
+               }
+       }
+}
+
+class AltosSites extends Thread {
+       AltosSiteMapPreload     preload;
+       URL                     url;
+       LinkedList<AltosSite>   sites;
+
+       void notify_complete() {
+               SwingUtilities.invokeLater(new Runnable() {
+                               public void run() {
+                                       preload.set_sites();
+                               }
+                       });
+       }
+
+       void add(AltosSite site) {
+               sites.add(site);
+       }
+
+       void add(String line) {
+               try {
+                       add(new AltosSite(line));
+               } catch (ParseException pe) {
+               }
+       }
+
+       public void run() {
+               try {
+                       URLConnection uc = url.openConnection();
+                       int length = uc.getContentLength();
+                       
+                       InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), Altos.unicode_set);
+                       BufferedReader in = new BufferedReader(in_stream);
+
+                       for (;;) {
+                               String line = in.readLine();
+                               if (line == null)
+                                       break;
+                               add(line);
+                       }
+               } catch (IOException e) {
+               } finally {
+                       notify_complete();
+               }
+       }
+
+       public AltosSites(AltosSiteMapPreload in_preload) {
+               sites = new LinkedList<AltosSite>();
+               preload = in_preload;
+               try {
+                       url = new URL(Altos.launch_sites_url);
+               } catch (java.net.MalformedURLException e) {
+                       notify_complete();
+               }
+               start();
+       }
+}
+
+public class AltosSiteMapPreload extends AltosDialog implements ActionListener, ItemListener {
+       AltosUI         owner;
+       AltosSiteMap    map;
+
+       AltosMapPos     lat;
+       AltosMapPos     lon;
+
+       final static int        radius = 4;
+       final static int        width = (radius * 2 + 1);
+       final static int        height = (radius * 2 + 1);
+
+       JProgressBar    pbar;
+
+       AltosSites      sites;
+       JLabel          site_list_label;
+       JComboBox       site_list;
+               
+       JToggleButton   load_button;
+       boolean         loading;
+       JButton         close_button;
+
+       static final String[]   lat_hemi_names = { "N", "S" };
+       static final String[]   lon_hemi_names = { "E", "W" };
+
+       class updatePbar implements Runnable {
+               int             n;
+               String          s;
+
+               public updatePbar(int x, int y, String in_s) {
+                       n = (x + radius) + (y + radius) * width + 1;
+                       s = in_s;
+               }
+
+               public void run() {
+                       pbar.setValue(n);
+                       pbar.setString(s);
+                       if (n < width * height) {
+                               pbar.setValue(n);
+                               pbar.setString(s);
+                       } else {
+                               pbar.setValue(0);
+                               pbar.setString("");
+                               load_button.setSelected(false);
+                               loading = false;
+                       }
+               }
+       }
+
+       class bgLoad extends Thread {
+
+               AltosSiteMap    map;
+
+               public bgLoad(AltosSiteMap in_map) {
+                       map = in_map;
+               }
+
+               public void run() {
+                       for (int y = -map.radius; y <= map.radius; y++) {
+                               for (int x = -map.radius; x <= map.radius; x++) {
+                                       String  pngfile;
+                                       pngfile = map.initMap(new Point(x,y));
+                                       SwingUtilities.invokeLater(new updatePbar(x, y, pngfile));
+                               }
+                       }
+               }
+       }
+
+       public void set_sites() {
+               int     i = 1;
+               for (AltosSite site : sites.sites) {
+                       site_list.insertItemAt(site, i);
+                       i++;
+               }
+       }
+
+       public void itemStateChanged(ItemEvent e) {
+               int             state = e.getStateChange();
+
+               if (state == ItemEvent.SELECTED) {
+                       Object  o = e.getItem();
+                       if (o instanceof AltosSite) {
+                               AltosSite       site = (AltosSite) o;
+                               lat.set_value(site.latitude);
+                               lon.set_value(site.longitude);
+                       }
+               }
+       }
+
+       public void actionPerformed(ActionEvent e) {
+               String  cmd = e.getActionCommand();
+
+               if (cmd.equals("close"))
+                       setVisible(false);
+
+               if (cmd.equals("load")) {
+                       if (!loading) {
+                               try {
+                                       final double    latitude = lat.get_value();
+                                       final double    longitude = lon.get_value();
+                                       map.setBaseLocation(latitude,longitude);
+                                       map.draw_circle(latitude,longitude);
+                                       loading = true;
+                                       bgLoad thread = new bgLoad(map);
+                                       thread.start();
+                               } catch (NumberFormatException ne) {
+                                       load_button.setSelected(false);
+                               }
+                       }
+               }
+       }
+
+       public AltosSiteMapPreload(AltosUI in_owner) {
+               owner = in_owner;
+
+               Container               pane = getContentPane();
+               GridBagConstraints      c = new GridBagConstraints();
+               Insets                  i = new Insets(4,4,4,4);
+
+               pane.setLayout(new GridBagLayout());
+
+               map = new AltosSiteMap(4);
+
+               c.fill = GridBagConstraints.BOTH;
+               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;
+
+               pane.add(map, c);
+
+               pbar = new JProgressBar();
+               pbar.setMinimum(0);
+               pbar.setMaximum(width * height);
+               pbar.setValue(0);
+               pbar.setString("");
+               pbar.setStringPainted(true);
+               
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 1;
+               c.gridwidth = 2;
+
+               pane.add(pbar, c);
+
+               site_list_label = new JLabel ("Known Launch Sites:");
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 2;
+               c.gridwidth = 1;
+
+               pane.add(site_list_label, c);
+               
+               site_list = new JComboBox(new String[] { "Site List" });
+               site_list.addItemListener(this);
+
+               sites = new AltosSites(this);
+
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 1;
+               c.gridy = 2;
+               c.gridwidth = 1;
+
+               pane.add(site_list, c);
+               
+               lat = new AltosMapPos(owner,
+                                     "Latitude:",
+                                     lat_hemi_names,
+                                     37.167833333);
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 0;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(lat, c);
+               
+               lon = new AltosMapPos(owner,
+                                     "Longitude:",
+                                     lon_hemi_names,
+                                     -97.73975);
+
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 0;
+               c.weighty = 0;
+
+               c.gridx = 1;
+               c.gridy = 3;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(lon, c);
+
+               load_button = new JToggleButton("Load Map");
+               load_button.addActionListener(this);
+               load_button.setActionCommand("load");
+               
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 0;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(load_button, c);
+
+               close_button = new JButton("Close");
+               close_button.addActionListener(this);
+               close_button.setActionCommand("close");
+               
+               c.fill = GridBagConstraints.NONE;
+               c.anchor = GridBagConstraints.CENTER;
+               c.insets = i;
+               c.weightx = 1;
+               c.weighty = 0;
+
+               c.gridx = 1;
+               c.gridy = 4;
+               c.gridwidth = 1;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(close_button, c);
+
+               pack();
+               setLocationRelativeTo(owner);
+               setVisible(true);
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosSiteMapTile.java b/altosui/AltosSiteMapTile.java
new file mode 100644 (file)
index 0000000..3455021
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * 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;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosSiteMapTile extends JLayeredPane {
+       JLabel mapLabel;
+       JLabel draw;
+       Graphics2D g2d;
+       int px_size;
+
+       public void loadMap(ImageIcon icn) {
+               mapLabel.setIcon(icn);
+       }
+
+       public void clearMap() {
+               fillLabel(mapLabel, Color.GRAY, px_size);
+               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));
+       }
+
+       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 void draw_circle(Point2D.Double pt) {
+               g2d.setColor(Color.RED);
+               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);
+       }
+
+       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 in_px_size) {
+               px_size = in_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/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub b/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub
new file mode 100755 (executable)
index 0000000..c661d3e
Binary files /dev/null and b/altosui/AltosUI.app/Contents/MacOS/JavaApplicationStub differ
diff --git a/altosui/AltosUI.app/Contents/PkgInfo b/altosui/AltosUI.app/Contents/PkgInfo
new file mode 100644 (file)
index 0000000..8a43480
--- /dev/null
@@ -0,0 +1 @@
+APPLAM.O
diff --git a/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns b/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns
new file mode 100644 (file)
index 0000000..fe49f36
Binary files /dev/null and b/altosui/AltosUI.app/Contents/Resources/AltosUIIcon.icns differ
diff --git a/altosui/AltosUI.java b/altosui/AltosUI.java
new file mode 100644 (file)
index 0000000..926d66f
--- /dev/null
@@ -0,0 +1,603 @@
+/*
+ * 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.*;
+import org.altusmetrum.AltosLib.*;
+
+import libaltosJNI.*;
+
+public class AltosUI extends AltosFrame {
+       public AltosVoice voice = new AltosVoice();
+
+       public static boolean load_library(Frame frame) {
+               if (!Altos.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(new AltosSerial(device));
+                       if (reader != null)
+                               new AltosFlightUI(voice, reader, device.getSerial());
+               } catch (FileNotFoundException ee) {
+                       JOptionPane.showMessageDialog(AltosUI.this,
+                                                     ee.getMessage(),
+                                                     "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);
+               } catch (TimeoutException te) {
+                       JOptionPane.showMessageDialog(this,
+                                                     device.toShortString(),
+                                                     "Timeout error",
+                                                     JOptionPane.ERROR_MESSAGE);
+               } catch (InterruptedException ie) {
+                       JOptionPane.showMessageDialog(this,
+                                                     device.toShortString(),
+                                                     "Interrupted exception",
+                                                     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());
+
+               AltosUIPreferences.set_component(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.setToolTipText("Connect to TeleDongle and monitor telemetry");
+               b = addButton(1, 0, "Save Flight Data");
+               b.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               SaveFlightData();
+                                       }
+                               });
+               b.setToolTipText("Download and/or delete flight data from an altimeter");
+               b = addButton(2, 0, "Replay Flight");
+               b.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               Replay();
+                                       }
+                               });
+               b.setToolTipText("Watch an old flight in real-time");
+               b = addButton(3, 0, "Graph Data");
+               b.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               GraphData();
+                                       }
+                               });
+               b.setToolTipText("Present flight data in a graph and table of statistics");
+               b = addButton(4, 0, "Export Data");
+               b.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               ExportData();
+                                       }
+                               });
+               b.setToolTipText("Convert flight data for a spreadsheet or GoogleEarth");
+               b = addButton(0, 1, "Configure Altimeter");
+               b.addActionListener(new ActionListener() {
+                                       public void actionPerformed(ActionEvent e) {
+                                               ConfigureTeleMetrum();
+                                       }
+                               });
+               b.setToolTipText("Set flight, storage and communication parameters");
+               b = addButton(1, 1, "Configure AltosUI");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       ConfigureAltosUI();
+                               }
+                       });
+               b.setToolTipText("Global AltosUI settings");
+
+               b = addButton(2, 1, "Configure Ground Station");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       ConfigureTeleDongle();
+                               }
+                       });
+
+               b = addButton(3, 1, "Flash Image");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       FlashImage();
+                               }
+                       });
+               b.setToolTipText("Replace the firmware in any AltusMetrum product");
+
+               b = addButton(4, 1, "Fire Igniter");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       FireIgniter();
+                               }
+                       });
+               b.setToolTipText("Remote control of igniters for deployment testing");
+               b = addButton(0, 2, "Scan Channels");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       ScanChannels();
+                               }
+                       });
+               b.setToolTipText("Find what channel an altimeter is sending telemetry on");
+               b = addButton(1, 2, "Load Maps");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       LoadMaps();
+                               }
+                       });
+               b.setToolTipText("Download satellite images for off-line flight monitoring");
+               b = addButton(2, 2, "Monitor Idle");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       IdleMonitor();
+                               }
+                       });
+               b.setToolTipText("Check flight readiness of altimeter in idle mode");
+
+//             b = addButton(3, 2, "Launch Controller");
+//             b.addActionListener(new ActionListener() {
+//                             public void actionPerformed(ActionEvent e) {
+//                                     LaunchController();
+//                             }
+//                     });
+
+               b = addButton(4, 2, "Quit");
+               b.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       System.exit(0);
+                               }
+                       });
+               b.setToolTipText("Close all active windows and terminate AltosUI");
+
+               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,
+                                                               Altos.product_basestation);
+
+               if (device != null)
+                       telemetry_window(device);
+       }
+
+       void ConfigureCallsign() {
+               String  result;
+               result = JOptionPane.showInputDialog(AltosUI.this,
+                                                    "Configure Callsign",
+                                                    AltosUIPreferences.callsign());
+               if (result != null)
+                       AltosUIPreferences.set_callsign(result);
+       }
+
+       void ConfigureTeleMetrum() {
+               new AltosConfig(AltosUI.this);
+       }
+
+       void ConfigureTeleDongle() {
+               new AltosConfigTD(AltosUI.this);
+       }
+
+       void FlashImage() {
+               AltosFlashUI.show(AltosUI.this);
+       }
+
+       void FireIgniter() {
+               new AltosIgniteUI(AltosUI.this);
+       }
+
+       void ScanChannels() {
+               new AltosScanUI(AltosUI.this);
+       }
+
+       void LoadMaps() {
+               new AltosSiteMapPreload(AltosUI.this);
+       }
+
+       void LaunchController() {
+               new AltosLaunchUI(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.file());
+                       new AltosFlightUI(voice, reader);
+               }
+       }
+
+       /* Connect to TeleMetrum, either directly or through
+        * a TeleDongle over the packet link
+        */
+       private void SaveFlightData() {
+               new AltosEepromManage(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;
+               try {
+                       new AltosGraphUI(record_reader, chooser.filename());
+               } catch (InterruptedException ie) {
+               } catch (IOException ie) {
+               }
+       }
+
+       private void ConfigureAltosUI() {
+               new AltosConfigureUI(AltosUI.this, voice);
+       }
+
+       private void IdleMonitor() {
+               try {
+                       new AltosIdleMonitorUI(this);
+               } catch (Exception e) {
+               }
+       }
+
+       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 if (filename.endsWith("mega"))
+                               return new AltosEepromMegaIterable(in);
+                       else
+                               return new AltosTelemetryIterable(in);
+               } catch (FileNotFoundException fe) {
+                       System.out.printf("%s\n", fe.getMessage());
+                       return null;
+               }
+       }
+
+       static AltosWriter open_csv(String filename) {
+               File file = new File (filename);
+               try {
+                       return new AltosCSV(file);
+               } catch (FileNotFoundException fe) {
+                       System.out.printf("%s\n", fe.getMessage());
+                       return null;
+               }
+       }
+
+       static AltosWriter open_kml(String filename) {
+               File file = new File (filename);
+               try {
+                       return new AltosKML(file);
+               } catch (FileNotFoundException fe) {
+                       System.out.printf("%s\n", fe.getMessage());
+                       return null;
+               }
+       }
+
+       static final int process_none = 0;
+       static final int process_csv = 1;
+       static final int process_kml = 2;
+       static final int process_graph = 3;
+       static final int process_replay = 4;
+       static final int process_summary = 5;
+
+       static void process_csv(String input) {
+               AltosRecordIterable iterable = open_logfile(input);
+               if (iterable == null)
+                       return;
+
+               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)
+                               return;
+                       writer.write(iterable);
+                       writer.close();
+               }
+       }
+
+       static void process_kml(String input) {
+               AltosRecordIterable iterable = open_logfile(input);
+               if (iterable == null)
+                       return;
+
+               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();
+               }
+       }
+
+       static AltosRecordIterable record_iterable(File file) {
+               FileInputStream in;
+               try {
+                       in = new FileInputStream(file);
+               } catch (Exception e) {
+                       System.out.printf("Failed to open file '%s'\n", file);
+                       return null;
+               }
+               AltosRecordIterable recs;
+               AltosReplayReader reader;
+               if (file.getName().endsWith("eeprom")) {
+                       recs = new AltosEepromIterable(in);
+               } else {
+                       recs = new AltosTelemetryIterable(in);
+               }
+               return recs;
+       }
+
+       static AltosRecordIterable record_iterable_file(String filename) {
+               return record_iterable (new File(filename));
+       }
+
+       static AltosReplayReader replay_file(String filename) {
+               AltosRecordIterable recs = record_iterable_file(filename);
+               if (recs == null)
+                       return null;
+               return new AltosReplayReader(recs.iterator(), new File(filename));
+       }
+
+       static void process_replay(String filename) {
+               AltosReplayReader reader = replay_file(filename);
+               AltosFlightUI flight_ui = new AltosFlightUI(new AltosVoice(), reader);
+               flight_ui.set_exit_on_close();
+       }
+
+       static void process_graph(String filename) {
+               AltosRecordIterable recs = record_iterable_file(filename);
+               if (recs == null)
+                       return;
+               try {
+                       new AltosGraphUI(recs, filename);
+               } catch (InterruptedException ie) {
+               } catch (IOException ie) {
+               }
+       }
+       
+       static void process_summary(String filename) {
+               AltosRecordIterable iterable = record_iterable_file(filename);
+               try {
+                       AltosFlightStats stats = new AltosFlightStats(iterable);
+                       if (stats.serial > 0)
+                               System.out.printf("Serial:       %5d\n", stats.serial);
+                       if (stats.flight > 0)
+                               System.out.printf("Flight:       %5d\n", stats.flight);
+                       if (stats.year > 0)
+                               System.out.printf("Date:    %04d-%02d-%02d\n",
+                                                 stats.year, stats.month, stats.day);
+                       if (stats.hour > 0)
+                               System.out.printf("Time:      %02d:%02d:%02d UTC\n",
+                                                 stats.hour, stats.minute, stats.second);
+                       System.out.printf("Max height:  %6.0f m    %6.0f ft\n",
+                                         stats.max_height,
+                                         AltosConvert.meters_to_feet(stats.max_height));
+                       System.out.printf("Max speed:   %6.0f m/s  %6.0f ft/s  %6.4f Mach\n",
+                                         stats.max_speed,
+                                         AltosConvert.meters_to_feet(stats.max_speed),
+                                         AltosConvert.meters_to_mach(stats.max_speed));
+                       if (stats.max_acceleration != AltosRecord.MISSING) {
+                               System.out.printf("Max accel:   %6.0f m/s² %6.0f ft/s² %6.2f g\n",
+                                                 stats.max_acceleration,
+                                                 AltosConvert.meters_to_feet(stats.max_acceleration),
+                                                 AltosConvert.meters_to_g(stats.max_acceleration));
+                       }
+                       System.out.printf("Drogue rate: %6.0f m/s  %6.0f ft/s\n",
+                                         stats.state_baro_speed[Altos.ao_flight_drogue],
+                                         AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_drogue]));
+                       System.out.printf("Main rate:   %6.0f m/s  %6.0f ft/s\n",
+                                         stats.state_baro_speed[Altos.ao_flight_main],
+                                         AltosConvert.meters_to_feet(stats.state_baro_speed[Altos.ao_flight_main]));
+                       System.out.printf("Flight time: %6.0f s\n",
+                                         stats.state_end[Altos.ao_flight_main] -
+                                         stats.state_start[Altos.ao_flight_boost]);
+               } catch (InterruptedException ie) {
+               } catch (IOException ie) {
+               }
+       }
+
+       public static void help(int code) {
+               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("    --graph <filename>\t\tgraph a flight\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");
+               System.exit(code);
+       }
+       
+       public static void main(final String[] args) {
+               load_library(null);
+               try {
+                       UIManager.setLookAndFeel(AltosUIPreferences.look_and_feel());
+               } catch (Exception e) {
+               }
+               /* Handle batch-mode */
+               if (args.length == 0) {
+                       AltosUI altosui = new AltosUI();
+                       altosui.setVisible(true);
+
+                       java.util.List<AltosDevice> devices = AltosUSBDevice.list(Altos.product_basestation);
+                       for (AltosDevice device : devices)
+                               altosui.telemetry_window(device);
+               } else {
+                       int process = process_none;
+                       for (int i = 0; i < args.length; i++) {
+                               if (args[i].equals("--help"))
+                                       help(0);
+                               else if (args[i].equals("--fetchmaps")) {
+                                       if (args.length < i + 3) {
+                                               help(1);
+                                       } else {
+                                               double lat = Double.parseDouble(args[i+1]);
+                                               double lon = Double.parseDouble(args[i+2]);
+                                               AltosSiteMap.prefetchMaps(lat, lon, 5, 5);
+                                               i += 2;
+                                       }
+                               } else if (args[i].equals("--replay"))
+                                       process = process_replay;
+                               else if (args[i].equals("--kml"))
+                                       process = process_kml;
+                               else if (args[i].equals("--csv"))
+                                       process = process_csv;
+                               else if (args[i].equals("--graph"))
+                                       process = process_graph;
+                               else if (args[i].equals("--summary"))
+                                       process = process_summary;
+                               else if (args[i].startsWith("--"))
+                                       help(1);
+                               else {
+                                       switch (process) {
+                                       case process_none:
+                                       case process_graph:
+                                               process_graph(args[i]);
+                                               break;
+                                       case process_replay:
+                                               process_replay(args[i]);
+                                               break;
+                                       case process_kml:
+                                               process_kml(args[i]);
+                                               break;
+                                       case process_csv:
+                                               process_csv(args[i]);
+                                               break;
+                                       case process_summary:
+                                               process_summary(args[i]);
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+}
diff --git a/altosui/AltosUIListener.java b/altosui/AltosUIListener.java
new file mode 100644 (file)
index 0000000..7ee62af
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2011 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 AltosUIListener {
+       public void ui_changed(String look_and_feel);
+}
diff --git a/altosui/AltosUIPreferences.java b/altosui/AltosUIPreferences.java
new file mode 100644 (file)
index 0000000..10ab26c
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2011 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.*;
+import java.text.*;
+import java.util.prefs.*;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.awt.Component;
+import javax.swing.*;
+import javax.swing.filechooser.FileSystemView;
+import org.altusmetrum.AltosLib.*;
+
+public class AltosUIPreferences extends AltosPreferences {
+
+       /* font size preferences name */
+       final static String fontSizePreference = "FONT-SIZE";
+
+       /* Look&Feel preference name */
+       final static String lookAndFeelPreference = "LOOK-AND-FEEL";
+
+       /* UI Component to pop dialogs up */
+       static Component component;
+
+       static LinkedList<AltosFontListener> font_listeners;
+
+       static int font_size = Altos.font_size_medium;
+
+       static LinkedList<AltosUIListener> ui_listeners;
+
+       static String look_and_feel = null;
+
+       /* Serial debug */
+       static boolean serial_debug;
+
+       public static void init() {
+               font_listeners = new LinkedList<AltosFontListener>();
+
+               font_size = preferences.getInt(fontSizePreference, Altos.font_size_medium);
+               Altos.set_fonts(font_size);
+               look_and_feel = preferences.get(lookAndFeelPreference, UIManager.getSystemLookAndFeelClassName());
+
+               ui_listeners = new LinkedList<AltosUIListener>();
+               serial_debug = preferences.getBoolean(serialDebugPreference, false);
+               AltosLink.set_debug(serial_debug);
+       }
+
+       static { init(); }
+
+       static void set_component(Component in_component) {
+               component = in_component;
+       }
+
+       private static boolean check_dir(File dir) {
+               if (!dir.exists()) {
+                       if (!dir.mkdirs()) {
+                               JOptionPane.showMessageDialog(component,
+                                                             dir.getName(),
+                                                             "Cannot create directory",
+                                                             JOptionPane.ERROR_MESSAGE);
+                               return false;
+                       }
+               } else if (!dir.isDirectory()) {
+                       JOptionPane.showMessageDialog(component,
+                                                     dir.getName(),
+                                                     "Is not a directory",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       return false;
+               }
+               return true;
+       }
+
+       /* Configure the log directory. This is where all telemetry and eeprom files
+        * will be written to, and where replay will look for telemetry files
+        */
+       public static void ConfigureLog() {
+               JFileChooser    logdir_chooser = new JFileChooser(logdir.getParentFile());
+
+               logdir_chooser.setDialogTitle("Configure Data Logging Directory");
+               logdir_chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
+
+               if (logdir_chooser.showDialog(component, "Select Directory") == JFileChooser.APPROVE_OPTION) {
+                       File dir = logdir_chooser.getSelectedFile();
+                       if (check_dir(dir))
+                               set_logdir(dir);
+               }
+       }
+       public static int font_size() {
+               return font_size;
+       }
+
+       static void set_fonts() {
+       }
+
+       public static void set_font_size(int new_font_size) {
+               font_size = new_font_size;
+               synchronized (preferences) {
+                       preferences.putInt(fontSizePreference, font_size);
+                       flush_preferences();
+                       Altos.set_fonts(font_size);
+                       for (AltosFontListener l : font_listeners)
+                               l.font_size_changed(font_size);
+               }
+       }
+
+       public static void register_font_listener(AltosFontListener l) {
+               synchronized (preferences) {
+                       font_listeners.add(l);
+               }
+       }
+
+       public static void unregister_font_listener(AltosFontListener l) {
+               synchronized (preferences) {
+                       font_listeners.remove(l);
+               }
+       }
+
+       public static void set_look_and_feel(String new_look_and_feel) {
+               look_and_feel = new_look_and_feel;
+               try {
+                       UIManager.setLookAndFeel(look_and_feel);
+               } catch (Exception e) {
+               }
+               synchronized(preferences) {
+                       preferences.put(lookAndFeelPreference, look_and_feel);
+                       flush_preferences();
+                       for (AltosUIListener l : ui_listeners)
+                               l.ui_changed(look_and_feel);
+               }
+       }
+
+       public static String look_and_feel() {
+               return look_and_feel;
+       }
+
+       public static void register_ui_listener(AltosUIListener l) {
+               synchronized(preferences) {
+                       ui_listeners.add(l);
+               }
+       }
+
+       public static void unregister_ui_listener(AltosUIListener l) {
+               synchronized (preferences) {
+                       ui_listeners.remove(l);
+               }
+       }
+       public static void set_serial_debug(boolean new_serial_debug) {
+               serial_debug = new_serial_debug;
+               AltosLink.set_debug(serial_debug);
+               synchronized (preferences) {
+                       preferences.putBoolean(serialDebugPreference, serial_debug);
+                       flush_preferences();
+               }
+       }
+
+       public static boolean serial_debug() {
+               return serial_debug;
+       }
+
+}
\ No newline at end of file
diff --git a/altosui/AltosUSBDevice.java b/altosui/AltosUSBDevice.java
new file mode 100644 (file)
index 0000000..ed5f830
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * 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.util.*;
+import libaltosJNI.*;
+
+public class AltosUSBDevice  extends altos_device implements AltosDevice {
+
+       public String toString() {
+               String  name = getName();
+               if (name == null)
+                       name = "Altus Metrum";
+               return String.format("%-20.20s %4d %s",
+                                    name, getSerial(), getPath());
+       }
+
+       public String toShortString() {
+               String  name = getName();
+               if (name == null)
+                       name = "Altus Metrum";
+               return String.format("%s %d %s",
+                                    name, getSerial(), getPath());
+
+       }
+
+       public String getErrorString() {
+               altos_error     error = new altos_error();
+
+               libaltos.altos_get_last_error(error);
+               return String.format("%s (%d)", error.getString(), error.getCode());
+       }
+
+       public SWIGTYPE_p_altos_file open() {
+               return libaltos.altos_open(this);
+       }
+
+       private boolean isAltusMetrum() {
+               if (getVendor() != Altos.vendor_altusmetrum)
+                       return false;
+               if (getProduct() < Altos.product_altusmetrum_min)
+                       return false;
+               if (getProduct() > Altos.product_altusmetrum_max)
+                       return false;
+               return true;
+       }
+
+       public boolean matchProduct(int want_product) {
+
+               if (!isAltusMetrum())
+                       return false;
+
+               if (want_product == Altos.product_any)
+                       return true;
+
+               if (want_product == Altos.product_basestation)
+                       return matchProduct(Altos.product_teledongle) ||
+                               matchProduct(Altos.product_teleterra) ||
+                               matchProduct(Altos.product_telebt) ||
+                               matchProduct(Altos.product_megadongle);
+
+               if (want_product == Altos.product_altimeter)
+                       return matchProduct(Altos.product_telemetrum) ||
+                               matchProduct(Altos.product_megametrum);
+
+               int have_product = getProduct();
+
+               if (have_product == Altos.product_altusmetrum)  /* old devices match any request */
+                       return true;
+
+               if (want_product == have_product)
+                       return true;
+
+               return false;
+       }
+
+       static java.util.List<AltosDevice> list(int product) {
+               if (!Altos.load_library())
+                       return null;
+
+               SWIGTYPE_p_altos_list list = libaltos.altos_list_start();
+
+               ArrayList<AltosDevice> device_list = new ArrayList<AltosDevice>();
+               if (list != null) {
+                       for (;;) {
+                               AltosUSBDevice device = new AltosUSBDevice();
+                               if (libaltos.altos_list_next(list, device) == 0)
+                                       break;
+                               if (device.matchProduct(product))
+                                       device_list.add(device);
+                       }
+                       libaltos.altos_list_finish(list);
+               }
+
+               return device_list;
+       }
+}
\ No newline at end of file
diff --git a/altosui/AltosVersion.java.in b/altosui/AltosVersion.java.in
new file mode 100644 (file)
index 0000000..b0b3c0c
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2011 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 class AltosVersion {
+       public final static String version = "@VERSION@";
+}
diff --git a/altosui/AltosVoice.java b/altosui/AltosVoice.java
new file mode 100644 (file)
index 0000000..ab74e0b
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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 com.sun.speech.freetts.Voice;
+import com.sun.speech.freetts.VoiceManager;
+import com.sun.speech.freetts.audio.JavaClipAudioPlayer;
+import java.util.concurrent.LinkedBlockingQueue;
+
+public class AltosVoice implements Runnable {
+       VoiceManager                    voice_manager;
+       Voice                           voice;
+       LinkedBlockingQueue<String>     phrases;
+       Thread                          thread;
+       boolean                         busy;
+
+       final static String voice_name = "kevin16";
+
+       public void run() {
+               try {
+                       for (;;) {
+                               String s = phrases.take();
+                               voice.speak(s);
+                               synchronized(this) {
+                                       if (phrases.isEmpty()) {
+                                               busy = false;
+                                               notifyAll();
+                                       }
+                               }
+                       }
+               } catch (InterruptedException e) {
+               }
+       }
+
+       public synchronized void drain() throws InterruptedException {
+               while (busy)
+                       wait();
+       }
+
+       public void speak_always(String s) {
+               try {
+                       if (voice != null) {
+                               synchronized(this) {
+                                       busy = true;
+                                       phrases.put(s);
+                               }
+                       }
+               } catch (InterruptedException e) {
+               }
+       }
+
+       public void speak(String s) {
+               if (AltosUIPreferences.voice())
+                       speak_always(s);
+       }
+
+       public void speak(String format, Object... parameters) {
+               speak(String.format(format, parameters));
+       }
+
+       public AltosVoice () {
+               busy = false;
+               voice_manager = VoiceManager.getInstance();
+               voice = voice_manager.getVoice(voice_name);
+               if (voice != null) {
+                       voice.allocate();
+                       phrases = new LinkedBlockingQueue<String> ();
+                       thread = new Thread(this);
+                       thread.start();
+               } else {
+                       System.out.printf("Voice manager failed to open %s\n", voice_name);
+                       Voice[] voices = voice_manager.getVoices();
+                       System.out.printf("Available voices:\n");
+                       for (int i = 0; i < voices.length; i++) {
+                               System.out.println("    " + voices[i].getName()
+                                                  + " (" + voices[i].getDomain() + " domain)");
+                       }
+               }
+       }
+}
diff --git a/altosui/AltosWriter.java b/altosui/AltosWriter.java
new file mode 100644 (file)
index 0000000..b737520
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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.io.*;
+import java.text.*;
+import java.util.*;
+import org.altusmetrum.AltosLib.*;
+
+
+public interface AltosWriter {
+
+       public void write(AltosRecord record);
+
+       public void write(AltosRecordIterable iterable);
+
+       public void close();
+}
diff --git a/altosui/GrabNDrag.java b/altosui/GrabNDrag.java
new file mode 100644 (file)
index 0000000..c350efe
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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 org.altusmetrum.AltosLib.*;
+
+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/altosui/Info.plist.in b/altosui/Info.plist.in
new file mode 100644 (file)
index 0000000..46dea17
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+       <key>CFBundleName</key>
+       <string>AltosUI</string>
+       <key>CFBundleVersion</key>
+       <string>@VERSION@</string>
+       <key>CFBundleAllowMixedLocalizations</key>
+       <string>true</string>
+       <key>CFBundleExecutable</key>
+       <string>JavaApplicationStub</string>
+       <key>CFBundleDevelopmentRegion</key>
+       <string>English</string>
+       <key>CFBundlePackageType</key>
+       <string>APPL</string>
+       <key>CFBundleIdentifier</key>
+       <string>org.altusmetrum.altosui</string>
+       <key>CFBundleSignature</key>
+       <string>Altu</string>
+       <key>CFBundleGetInfoString</key>
+       <string>AltOS UI version @VERSION@</string>
+       <key>CFBundleInfoDictionaryVersion</key>
+       <string>6.0</string>
+       <key>CFBundleIconFile</key>
+       <string>AltosUIIcon.icns</string>
+       <key>Java</key>
+       <dict>
+               <key>MainClass</key>
+               <string>altosui.AltosUI</string>
+               <key>JVMVersion</key>
+               <string>1.5+</string>
+               <key>ClassPath</key>
+               <array>
+                       <string>$JAVAROOT/altosui.jar</string>
+                       <string>$JAVAROOT/freetts.jar</string>
+               </array>
+               <key>VMOptions</key>
+               <array>
+                 <string>-Xms512M</string>
+                 <string>-Xmx512M</string>
+                 <string>-Dosgi.clean=true</string>
+               </array>
+       </dict>
+</dict>
+</plist>
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi b/altosui/Instdrv/NSIS/Contrib/InstDrv/Example.nsi
new file mode 100644 (file)
index 0000000..3ed821e
--- /dev/null
@@ -0,0 +1,84 @@
+#\r
+# InstDrv Example, (c) 2003 Jan Kiszka (Jan Kiszka@web.de)\r
+#\r
+\r
+Name "InstDrv.dll test"\r
+\r
+OutFile "InstDrv-Test.exe"\r
+\r
+ShowInstDetails show\r
+\r
+ComponentText "InstDrv Plugin Usage Example"\r
+\r
+Page components\r
+Page instfiles\r
+\r
+Section "Install a Driver" InstDriver\r
+    InstDrv::InitDriverSetup /NOUNLOAD "{4d36e978-e325-11ce-bfc1-08002be10318}" "IrCOMM2k"\r
+    Pop $0\r
+    DetailPrint "InitDriverSetup: $0"\r
+\r
+    InstDrv::DeleteOemInfFiles /NOUNLOAD\r
+    Pop $0\r
+    DetailPrint "DeleteOemInfFiles: $0"\r
+    StrCmp $0 "00000000" PrintInfNames ContInst1\r
+\r
+  PrintInfNames:\r
+    Pop $0\r
+    DetailPrint "Deleted $0"\r
+    Pop $0\r
+    DetailPrint "Deleted $0"\r
+\r
+  ContInst1:\r
+    InstDrv::CreateDevice /NOUNLOAD\r
+    Pop $0\r
+    DetailPrint "CreateDevice: $0"\r
+\r
+    SetOutPath $TEMP\r
+    File "ircomm2k.inf"\r
+    File "ircomm2k.sys"\r
+\r
+    InstDrv::InstallDriver /NOUNLOAD "$TEMP\ircomm2k.inf"\r
+    Pop $0\r
+    DetailPrint "InstallDriver: $0"\r
+    StrCmp $0 "00000000" PrintReboot ContInst2\r
+\r
+  PrintReboot:\r
+    Pop $0\r
+    DetailPrint "Reboot: $0"\r
+\r
+  ContInst2:\r
+    InstDrv::CountDevices\r
+    Pop $0\r
+    DetailPrint "CountDevices: $0"\r
+SectionEnd\r
+\r
+Section "Uninstall the driver again" UninstDriver\r
+    InstDrv::InitDriverSetup /NOUNLOAD "{4d36e978-e325-11ce-bfc1-08002be10318}" "IrCOMM2k"\r
+    Pop $0\r
+    DetailPrint "InitDriverSetup: $0"\r
+\r
+    InstDrv::DeleteOemInfFiles /NOUNLOAD\r
+    Pop $0\r
+    DetailPrint "DeleteOemInfFiles: $0"\r
+    StrCmp $0 "00000000" PrintInfNames ContUninst1\r
+\r
+  PrintInfNames:\r
+    Pop $0\r
+    DetailPrint "Deleted $0"\r
+    Pop $0\r
+    DetailPrint "Deleted $0"\r
+\r
+  ContUninst1:\r
+    InstDrv::RemoveAllDevices\r
+    Pop $0\r
+    DetailPrint "RemoveAllDevices: $0"\r
+    StrCmp $0 "00000000" PrintReboot ContUninst2\r
+\r
+  PrintReboot:\r
+    Pop $0\r
+    DetailPrint "Reboot: $0"\r
+\r
+  ContUninst2:\r
+    Delete "$SYSDIR\system32\ircomm2k.sys"\r
+SectionEnd
\ No newline at end of file
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe
new file mode 100644 (file)
index 0000000..615bae1
Binary files /dev/null and b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv-Test.exe differ
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.c
new file mode 100644 (file)
index 0000000..efe866e
--- /dev/null
@@ -0,0 +1,704 @@
+/*\r
+\r
+InstDrv.dll - Installs or Removes Device Drivers\r
+\r
+Copyright © 2003 Jan Kiszka (Jan.Kiszka@web.de)\r
+\r
+This software is provided 'as-is', without any express or implied\r
+warranty. In no event will the authors be held liable for any damages\r
+arising from the use of this software.\r
+\r
+Permission is granted to anyone to use this software for any purpose,\r
+including commercial applications, and to alter it and redistribute\r
+it freely, subject to the following restrictions:\r
+\r
+1. The origin of this software must not be misrepresented; \r
+   you must not claim that you wrote the original software.\r
+   If you use this software in a product, an acknowledgment in the\r
+   product documentation would be appreciated but is not required.\r
+2. Altered versions must be plainly marked as such,\r
+   and must not be misrepresented as being the original software.\r
+3. This notice may not be removed or altered from any distribution.\r
+\r
+*/\r
+\r
+\r
+#include <windows.h>\r
+#include <setupapi.h>\r
+#include <newdev.h>\r
+#include "../exdll/exdll.h"\r
+\r
+\r
+char    paramBuf[1024];\r
+GUID    devClass;\r
+char    hwIdBuf[1024];\r
+int     initialized = 0;\r
+\r
+\r
+\r
+void* memset(void* dst, int val, unsigned int len)\r
+{\r
+    while (len-- > 0)\r
+        *((char *)dst)++ = val;\r
+\r
+    return NULL;\r
+}\r
+\r
+\r
+\r
+void* memcpy(void* dst, const void* src, unsigned int len)\r
+{\r
+    while (len-- > 0)\r
+        *((char *)dst)++ = *((char *)src)++;\r
+\r
+    return NULL;\r
+}\r
+\r
+\r
+\r
+int HexCharToInt(char c)\r
+{\r
+    if ((c >= '0') && (c <= '9'))\r
+        return c - '0';\r
+    else if ((c >= 'a') && (c <= 'f'))\r
+        return c - 'a' + 10;\r
+    else if ((c >= 'A') && (c <= 'F'))\r
+        return c - 'A' + 10;\r
+    else\r
+        return -1;\r
+}\r
+\r
+\r
+\r
+BOOLEAN HexStringToUInt(char* str, int width, void* valBuf)\r
+{\r
+    int i, val;\r
+\r
+\r
+    for (i = width - 4; i >= 0; i -= 4)\r
+    {\r
+        val = HexCharToInt(*str++);\r
+        if (val < 0)\r
+            return FALSE;\r
+        *(unsigned int *)valBuf += val << i;\r
+    }\r
+\r
+    return TRUE;\r
+}\r
+\r
+\r
+\r
+BOOLEAN StringToGUID(char* guidStr, GUID* pGuid)\r
+{\r
+    int i;\r
+\r
+\r
+    memset(pGuid, 0, sizeof(GUID));\r
+\r
+    if (*guidStr++ != '{')\r
+        return FALSE;\r
+\r
+    if (!HexStringToUInt(guidStr, 32, &pGuid->Data1))\r
+        return FALSE;\r
+    guidStr += 8;\r
+\r
+    if (*guidStr++ != '-')\r
+        return FALSE;\r
+\r
+    if (!HexStringToUInt(guidStr, 16, &pGuid->Data2))\r
+        return FALSE;\r
+    guidStr += 4;\r
+\r
+    if (*guidStr++ != '-')\r
+        return FALSE;\r
+\r
+    if (!HexStringToUInt(guidStr, 16, &pGuid->Data3))\r
+        return FALSE;\r
+    guidStr += 4;\r
+\r
+    if (*guidStr++ != '-')\r
+        return FALSE;\r
+\r
+    for (i = 0; i < 2; i++)\r
+    {\r
+        if (!HexStringToUInt(guidStr, 8, &pGuid->Data4[i]))\r
+            return FALSE;\r
+        guidStr += 2;\r
+    }\r
+\r
+    if (*guidStr++ != '-')\r
+        return FALSE;\r
+\r
+    for (i = 2; i < 8; i++)\r
+    {\r
+        if (!HexStringToUInt(guidStr, 8, &pGuid->Data4[i]))\r
+            return FALSE;\r
+        guidStr += 2;\r
+    }\r
+\r
+    if (*guidStr++ != '}')\r
+        return FALSE;\r
+\r
+    return TRUE;\r
+}\r
+\r
+\r
+\r
+DWORD FindNextDevice(HDEVINFO devInfoSet, SP_DEVINFO_DATA* pDevInfoData, DWORD* pIndex)\r
+{\r
+    DWORD   buffersize = 0;\r
+    LPTSTR  buffer     = NULL;\r
+    DWORD   dataType;\r
+    DWORD   result;\r
+\r
+\r
+    while (1)\r
+    {\r
+        if (!SetupDiEnumDeviceInfo(devInfoSet, (*pIndex)++, pDevInfoData))\r
+        {\r
+            result = GetLastError();\r
+            break;\r
+        }\r
+\r
+      GetDeviceRegistryProperty:\r
+        if (!SetupDiGetDeviceRegistryProperty(devInfoSet, pDevInfoData, SPDRP_HARDWAREID,\r
+                                              &dataType, (PBYTE)buffer, buffersize,\r
+                                              &buffersize))\r
+        {\r
+            result = GetLastError();\r
+\r
+            if (result == ERROR_INSUFFICIENT_BUFFER)\r
+            {\r
+                if (buffer != NULL)\r
+                    LocalFree(buffer);\r
+\r
+                buffer = (LPTSTR)LocalAlloc(LPTR, buffersize);\r
+\r
+                if (buffer == NULL)\r
+                    break;\r
+\r
+                goto GetDeviceRegistryProperty;\r
+            }\r
+            else if (result == ERROR_INVALID_DATA)\r
+                continue;   // ignore invalid entries\r
+            else\r
+                break;      // break on other errors\r
+        }\r
+\r
+        if (lstrcmpi(buffer, hwIdBuf) == 0)\r
+        {\r
+            result  = 0;\r
+            break;\r
+        }\r
+    }\r
+\r
+    if (buffer != NULL)\r
+        LocalFree(buffer);\r
+\r
+    return result;\r
+}\r
+\r
+\r
+\r
+DWORD FindFirstDevice(HWND hwndParent, const GUID* pDevClass, const LPTSTR hwId,\r
+                      HDEVINFO* pDevInfoSet, SP_DEVINFO_DATA* pDevInfoData,\r
+                      DWORD *pIndex, DWORD flags)\r
+{\r
+    DWORD   result;\r
+\r
+\r
+    *pDevInfoSet = SetupDiGetClassDevs((GUID*)pDevClass, NULL, hwndParent, flags);\r
+    if (*pDevInfoSet == INVALID_HANDLE_VALUE)\r
+        return GetLastError();\r
+\r
+    pDevInfoData->cbSize = sizeof(SP_DEVINFO_DATA);\r
+    *pIndex = 0;\r
+\r
+    result = FindNextDevice(*pDevInfoSet, pDevInfoData, pIndex);\r
+\r
+    if (result != 0)\r
+        SetupDiDestroyDeviceInfoList(*pDevInfoSet);\r
+\r
+    return result;\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::InitDriverSetup devClass drvHWID\r
+ *\r
+ *  devClass    - GUID of the driver's device setup class\r
+ *  drvHWID     - Hardware ID of the supported device\r
+ *\r
+ * Return:\r
+ *  result      - error message, empty on success\r
+ */\r
+void __declspec(dllexport) InitDriverSetup(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    EXDLL_INIT();\r
+\r
+    /* convert class GUID */\r
+    popstring(paramBuf);\r
+\r
+    if (!StringToGUID(paramBuf, &devClass))\r
+    {\r
+        popstring(paramBuf);\r
+        pushstring("Invalid GUID!");\r
+        return;\r
+    }\r
+\r
+    /* get hardware ID */\r
+    memset(hwIdBuf, 0, sizeof(hwIdBuf));\r
+    popstring(hwIdBuf);\r
+\r
+    initialized = 1;\r
+    pushstring("");\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::CountDevices\r
+ *\r
+ * Return:\r
+ *  result      - Number of installed devices the driver supports\r
+ */\r
+void __declspec(dllexport) CountDevices(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    HDEVINFO            devInfoSet;\r
+    SP_DEVINFO_DATA     devInfoData;\r
+    int                 count = 0;\r
+    char                countBuf[16];\r
+    DWORD               index;\r
+    DWORD               result;\r
+\r
+\r
+    EXDLL_INIT();\r
+\r
+    if (!initialized)\r
+    {\r
+        pushstring("Fatal error!");\r
+        return;\r
+    }\r
+\r
+    result = FindFirstDevice(hwndParent, &devClass, hwIdBuf, &devInfoSet, &devInfoData,\r
+                             &index, DIGCF_PRESENT);\r
+    if (result != 0)\r
+    {\r
+        pushstring("0");\r
+        return;\r
+    }\r
+\r
+    do\r
+    {\r
+        count++;\r
+    } while (FindNextDevice(devInfoSet, &devInfoData, &index) == 0);\r
+\r
+    SetupDiDestroyDeviceInfoList(devInfoSet);\r
+\r
+    wsprintf(countBuf, "%d", count);\r
+    pushstring(countBuf);\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::CreateDevice\r
+ *\r
+ * Return:\r
+ *  result      - Windows error code\r
+ */\r
+void __declspec(dllexport) CreateDevice(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    HDEVINFO            devInfoSet;\r
+    SP_DEVINFO_DATA     devInfoData;\r
+    DWORD               result = 0;\r
+    char                resultBuf[16];\r
+\r
+\r
+    EXDLL_INIT();\r
+\r
+    if (!initialized)\r
+    {\r
+        pushstring("Fatal error!");\r
+        return;\r
+    }\r
+\r
+    devInfoSet = SetupDiCreateDeviceInfoList(&devClass, hwndParent);\r
+    if (devInfoSet == INVALID_HANDLE_VALUE)\r
+    {\r
+        wsprintf(resultBuf, "%08X", GetLastError());\r
+        pushstring(resultBuf);\r
+        return;\r
+    }\r
+\r
+    devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);\r
+    if (!SetupDiCreateDeviceInfo(devInfoSet, hwIdBuf, &devClass, NULL,\r
+                                 hwndParent, DICD_GENERATE_ID, &devInfoData))\r
+    {\r
+        result = GetLastError();\r
+        goto InstallCleanup;\r
+    }\r
+\r
+    if (!SetupDiSetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_HARDWAREID,\r
+                                          hwIdBuf, (lstrlen(hwIdBuf)+2)*sizeof(TCHAR))) \r
+    {\r
+        result = GetLastError();\r
+        goto InstallCleanup;\r
+    }\r
+\r
+    if (!SetupDiCallClassInstaller(DIF_REGISTERDEVICE, devInfoSet, &devInfoData))\r
+        result = GetLastError();\r
+\r
+  InstallCleanup:\r
+    SetupDiDestroyDeviceInfoList(devInfoSet);\r
+\r
+    wsprintf(resultBuf, "%08X", result);\r
+    pushstring(resultBuf);\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::InstallDriver infPath\r
+ *\r
+ * Return:\r
+ *  result      - Windows error code\r
+ *  reboot      - non-zero if reboot is required\r
+ */\r
+void __declspec(dllexport) InstallDriver(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    char    resultBuf[16];\r
+    BOOL    reboot;\r
+\r
+\r
+    EXDLL_INIT();\r
+    popstring(paramBuf);\r
+\r
+    if (!initialized)\r
+    {\r
+        pushstring("Fatal error!");\r
+        return;\r
+    }\r
+\r
+    if (!UpdateDriverForPlugAndPlayDevices(hwndParent, hwIdBuf, paramBuf,\r
+                                           INSTALLFLAG_FORCE, &reboot))\r
+    {\r
+        wsprintf(resultBuf, "%08X", GetLastError());\r
+        pushstring(resultBuf);\r
+    }\r
+    else\r
+    {\r
+        wsprintf(resultBuf, "%d", reboot);\r
+        pushstring(resultBuf);\r
+        pushstring("00000000");\r
+    }\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::DeleteOemInfFiles\r
+ *\r
+ * Return:\r
+ *  result      - Windows error code\r
+ *  oeminf      - Path of the deleted devices setup file (oemXX.inf)\r
+ *  oempnf      - Path of the deleted devices setup file (oemXX.pnf)\r
+ */\r
+void __declspec(dllexport) DeleteOemInfFiles(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    HDEVINFO                devInfo;\r
+    SP_DEVINFO_DATA         devInfoData;\r
+    SP_DRVINFO_DATA         drvInfoData;\r
+    SP_DRVINFO_DETAIL_DATA  drvInfoDetail;\r
+    DWORD                   index;\r
+    DWORD                   result;\r
+    char                    resultBuf[16];\r
+\r
+\r
+    if (!initialized)\r
+    {\r
+        pushstring("Fatal error!");\r
+        return;\r
+    }\r
+\r
+    result = FindFirstDevice(NULL, &devClass, hwIdBuf, &devInfo, &devInfoData, &index, 0);\r
+    if (result != 0)\r
+        goto Cleanup1;\r
+\r
+    if (!SetupDiBuildDriverInfoList(devInfo, &devInfoData, SPDIT_COMPATDRIVER))\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup2;\r
+    }\r
+\r
+    drvInfoData.cbSize = sizeof(SP_DRVINFO_DATA);\r
+    drvInfoDetail.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);\r
+\r
+    if (!SetupDiEnumDriverInfo(devInfo, &devInfoData, SPDIT_COMPATDRIVER, 0, &drvInfoData))\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup3;\r
+    }\r
+\r
+    if (!SetupDiGetDriverInfoDetail(devInfo, &devInfoData, &drvInfoData,\r
+                                    &drvInfoDetail, sizeof(drvInfoDetail), NULL))\r
+    {\r
+        result = GetLastError();\r
+\r
+        if (result != ERROR_INSUFFICIENT_BUFFER)\r
+            goto Cleanup3;\r
+\r
+        result = 0;\r
+    }\r
+\r
+    pushstring(drvInfoDetail.InfFileName);\r
+    if (!DeleteFile(drvInfoDetail.InfFileName))\r
+        result = GetLastError();\r
+    else\r
+    {\r
+        index = lstrlen(drvInfoDetail.InfFileName);\r
+        if (index > 3)\r
+        {\r
+            lstrcpy(drvInfoDetail.InfFileName+index-3, "pnf");\r
+            pushstring(drvInfoDetail.InfFileName);\r
+            if (!DeleteFile(drvInfoDetail.InfFileName))\r
+                result = GetLastError();\r
+        }\r
+    }\r
+\r
+  Cleanup3:\r
+    SetupDiDestroyDriverInfoList(devInfo, &devInfoData, SPDIT_COMPATDRIVER);\r
+\r
+  Cleanup2:\r
+    SetupDiDestroyDeviceInfoList(devInfo);\r
+\r
+  Cleanup1:\r
+    wsprintf(resultBuf, "%08X", result);\r
+    pushstring(resultBuf);\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::RemoveAllDevices\r
+ *\r
+ * Return:\r
+ *  result      - Windows error code\r
+ *  reboot      - non-zero if reboot is required\r
+ */\r
+void __declspec(dllexport) RemoveAllDevices(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    HDEVINFO                devInfo;\r
+    SP_DEVINFO_DATA         devInfoData;\r
+    DWORD                   index;\r
+    DWORD                   result;\r
+    char                    resultBuf[16];\r
+    BOOL                    reboot = FALSE;\r
+    SP_DEVINSTALL_PARAMS    instParams;\r
+\r
+\r
+    EXDLL_INIT();\r
+\r
+    if (!initialized)\r
+    {\r
+        pushstring("Fatal error!");\r
+        return;\r
+    }\r
+\r
+    result = FindFirstDevice(NULL, &devClass, hwIdBuf, &devInfo, &devInfoData, &index, 0);\r
+    if (result != 0)\r
+        goto Cleanup1;\r
+\r
+    do\r
+    {\r
+        if (!SetupDiCallClassInstaller(DIF_REMOVE, devInfo, &devInfoData))\r
+        {\r
+            result = GetLastError();\r
+            break;\r
+        }\r
+\r
+        instParams.cbSize = sizeof(instParams);\r
+        if (!reboot &&\r
+            SetupDiGetDeviceInstallParams(devInfo, &devInfoData, &instParams) &&\r
+            ((instParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT)) != 0))\r
+        {\r
+            reboot = TRUE;\r
+        }\r
+\r
+        result = FindNextDevice(devInfo, &devInfoData, &index);\r
+    } while (result == 0);\r
+\r
+    SetupDiDestroyDeviceInfoList(devInfo);\r
+\r
+  Cleanup1:\r
+    if ((result == 0) || (result == ERROR_NO_MORE_ITEMS))\r
+    {\r
+        wsprintf(resultBuf, "%d", reboot);\r
+        pushstring(resultBuf);\r
+        pushstring("00000000");\r
+    }\r
+    else\r
+    {\r
+        wsprintf(resultBuf, "%08X", result);\r
+        pushstring(resultBuf);\r
+    }\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::StartSystemService serviceName\r
+ *\r
+ * Return:\r
+ *  result      - Windows error code\r
+ */\r
+void __declspec(dllexport) StartSystemService(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    SC_HANDLE       managerHndl;\r
+    SC_HANDLE       svcHndl;\r
+    SERVICE_STATUS  svcStatus;\r
+    DWORD           oldCheckPoint;\r
+    DWORD           result;\r
+    char            resultBuf[16];\r
+\r
+\r
+    EXDLL_INIT();\r
+    popstring(paramBuf);\r
+\r
+    managerHndl = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);\r
+    if (managerHndl == NULL)\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup1;\r
+    }\r
+\r
+    svcHndl = OpenService(managerHndl, paramBuf, SERVICE_START | SERVICE_QUERY_STATUS);\r
+    if (svcHndl == NULL)\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup2;\r
+    }\r
+\r
+    if (!StartService(svcHndl, 0, NULL) || !QueryServiceStatus(svcHndl, &svcStatus))\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup3;\r
+    }\r
+\r
+    while (svcStatus.dwCurrentState == SERVICE_START_PENDING)\r
+    {\r
+        oldCheckPoint = svcStatus.dwCheckPoint;\r
+\r
+        Sleep(svcStatus.dwWaitHint);\r
+\r
+        if (!QueryServiceStatus(svcHndl, &svcStatus))\r
+        {\r
+            result = GetLastError();\r
+            break;\r
+        }\r
+\r
+        if (oldCheckPoint >= svcStatus.dwCheckPoint)\r
+        {\r
+            if ((svcStatus.dwCurrentState == SERVICE_STOPPED) &&\r
+                (svcStatus.dwWin32ExitCode != 0))\r
+                result = svcStatus.dwWin32ExitCode;\r
+            else\r
+                result = ERROR_SERVICE_REQUEST_TIMEOUT;\r
+        }\r
+    }\r
+\r
+    if (svcStatus.dwCurrentState == SERVICE_RUNNING)\r
+        result = 0;\r
+\r
+  Cleanup3:\r
+    CloseServiceHandle(svcHndl);\r
+\r
+  Cleanup2:\r
+    CloseServiceHandle(managerHndl);\r
+\r
+  Cleanup1:\r
+    wsprintf(resultBuf, "%08X", result);\r
+    pushstring(resultBuf);\r
+}\r
+\r
+\r
+\r
+/*\r
+ * InstDrv::StopSystemService serviceName\r
+ *\r
+ * Return:\r
+ *  result      - Windows error code\r
+ */\r
+void __declspec(dllexport) StopSystemService(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)\r
+{\r
+    SC_HANDLE       managerHndl;\r
+    SC_HANDLE       svcHndl;\r
+    SERVICE_STATUS  svcStatus;\r
+    DWORD           oldCheckPoint;\r
+    DWORD           result;\r
+    char            resultBuf[16];\r
+\r
+\r
+    EXDLL_INIT();\r
+    popstring(paramBuf);\r
+\r
+    managerHndl = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);\r
+    if (managerHndl == NULL)\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup1;\r
+    }\r
+\r
+    svcHndl = OpenService(managerHndl, paramBuf, SERVICE_STOP | SERVICE_QUERY_STATUS);\r
+    if (svcHndl == NULL)\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup2;\r
+    }\r
+\r
+    if (!ControlService(svcHndl, SERVICE_CONTROL_STOP, &svcStatus))\r
+    {\r
+        result = GetLastError();\r
+        goto Cleanup3;\r
+    }\r
+\r
+    while (svcStatus.dwCurrentState == SERVICE_STOP_PENDING)\r
+    {\r
+        oldCheckPoint = svcStatus.dwCheckPoint;\r
+\r
+        Sleep(svcStatus.dwWaitHint);\r
+\r
+        if (!QueryServiceStatus(svcHndl, &svcStatus))\r
+        {\r
+            result = GetLastError();\r
+            break;\r
+        }\r
+\r
+        if (oldCheckPoint >= svcStatus.dwCheckPoint)\r
+        {\r
+            result = ERROR_SERVICE_REQUEST_TIMEOUT;\r
+            break;\r
+        }\r
+    }\r
+\r
+    if (svcStatus.dwCurrentState == SERVICE_STOPPED)\r
+        result = 0;\r
+\r
+  Cleanup3:\r
+    CloseServiceHandle(svcHndl);\r
+\r
+  Cleanup2:\r
+    CloseServiceHandle(managerHndl);\r
+\r
+  Cleanup1:\r
+    wsprintf(resultBuf, "%08X", result);\r
+    pushstring(resultBuf);\r
+}\r
+\r
+\r
+\r
+BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)\r
+{\r
+    return TRUE;\r
+}\r
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsp
new file mode 100644 (file)
index 0000000..874e66c
--- /dev/null
@@ -0,0 +1,110 @@
+# Microsoft Developer Studio Project File - Name="InstDrv" - Package Owner=<4>\r
+# Microsoft Developer Studio Generated Build File, Format Version 6.00\r
+# ** NICHT BEARBEITEN **\r
+\r
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102\r
+\r
+CFG=InstDrv - Win32 Debug\r
+!MESSAGE Dies ist kein gültiges Makefile. Zum Erstellen dieses Projekts mit NMAKE\r
+!MESSAGE verwenden Sie den Befehl "Makefile exportieren" und führen Sie den Befehl\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "InstDrv.mak".\r
+!MESSAGE \r
+!MESSAGE Sie können beim Ausführen von NMAKE eine Konfiguration angeben\r
+!MESSAGE durch Definieren des Makros CFG in der Befehlszeile. Zum Beispiel:\r
+!MESSAGE \r
+!MESSAGE NMAKE /f "InstDrv.mak" CFG="InstDrv - Win32 Debug"\r
+!MESSAGE \r
+!MESSAGE Für die Konfiguration stehen zur Auswahl:\r
+!MESSAGE \r
+!MESSAGE "InstDrv - Win32 Release" (basierend auf  "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE "InstDrv - Win32 Debug" (basierend auf  "Win32 (x86) Dynamic-Link Library")\r
+!MESSAGE \r
+\r
+# Begin Project\r
+# PROP AllowPerConfigDependencies 0\r
+# PROP Scc_ProjName ""\r
+# PROP Scc_LocalPath ""\r
+CPP=cl.exe\r
+MTL=midl.exe\r
+RSC=rc.exe\r
+\r
+!IF  "$(CFG)" == "InstDrv - Win32 Release"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 0\r
+# PROP BASE Output_Dir "Release"\r
+# PROP BASE Intermediate_Dir "Release"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 0\r
+# PROP Output_Dir "Release"\r
+# PROP Intermediate_Dir "Release"\r
+# PROP Ignore_Export_Lib 1\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /c\r
+# ADD CPP /nologo /MT /W3 /GX /O1 /I "C:\Programme\WINDDK\3790\inc\ddk\w2k" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /FD /c\r
+# SUBTRACT CPP /YX\r
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x407 /d "NDEBUG"\r
+# ADD RSC /l 0x407 /d "NDEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib comdlg32.lib advapi32.lib shell32.lib setupapi.lib newdev.lib /nologo /entry:"_DllMainCRTStartup" /dll /machine:I386 /nodefaultlib /out:"../../Plugins/InstDrv.dll" /libpath:"C:\Programme\WINDDK\3790\lib\w2k\i386" /opt:nowin98\r
+# SUBTRACT LINK32 /pdb:none\r
+\r
+!ELSEIF  "$(CFG)" == "InstDrv - Win32 Debug"\r
+\r
+# PROP BASE Use_MFC 0\r
+# PROP BASE Use_Debug_Libraries 1\r
+# PROP BASE Output_Dir "Debug"\r
+# PROP BASE Intermediate_Dir "Debug"\r
+# PROP BASE Target_Dir ""\r
+# PROP Use_MFC 0\r
+# PROP Use_Debug_Libraries 1\r
+# PROP Output_Dir "Debug"\r
+# PROP Intermediate_Dir "Debug"\r
+# PROP Ignore_Export_Lib 0\r
+# PROP Target_Dir ""\r
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /GZ  /c\r
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "INSTDRV_EXPORTS" /YX /FD /GZ  /c\r
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32\r
+# ADD BASE RSC /l 0x407 /d "_DEBUG"\r
+# ADD RSC /l 0x407 /d "_DEBUG"\r
+BSC32=bscmake.exe\r
+# ADD BASE BSC32 /nologo\r
+# ADD BSC32 /nologo\r
+LINK32=link.exe\r
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept\r
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"_DllMainCRTStartup" /dll /debug /machine:I386 /pdbtype:sept\r
+# SUBTRACT LINK32 /nodefaultlib\r
+\r
+!ENDIF \r
+\r
+# Begin Target\r
+\r
+# Name "InstDrv - Win32 Release"\r
+# Name "InstDrv - Win32 Debug"\r
+# Begin Group "Quellcodedateien"\r
+\r
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"\r
+# Begin Source File\r
+\r
+SOURCE=.\InstDrv.c\r
+# End Source File\r
+# End Group\r
+# Begin Group "Header-Dateien"\r
+\r
+# PROP Default_Filter "h;hpp;hxx;hm;inl"\r
+# End Group\r
+# Begin Group "Ressourcendateien"\r
+\r
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"\r
+# End Group\r
+# End Target\r
+# End Project\r
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw b/altosui/Instdrv/NSIS/Contrib/InstDrv/InstDrv.dsw
new file mode 100644 (file)
index 0000000..b3d02f0
--- /dev/null
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00\r
+# WARNUNG: DIESE ARBEITSBEREICHSDATEI DARF NICHT BEARBEITET ODER GELÖSCHT WERDEN!\r
+\r
+###############################################################################\r
+\r
+Project: "InstDrv"=.\InstDrv.dsp - Package Owner=<4>\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<4>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
+Global:\r
+\r
+Package=<5>\r
+{{{\r
+}}}\r
+\r
+Package=<3>\r
+{{{\r
+}}}\r
+\r
+###############################################################################\r
+\r
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt b/altosui/Instdrv/NSIS/Contrib/InstDrv/Readme.txt
new file mode 100644 (file)
index 0000000..e5877aa
--- /dev/null
@@ -0,0 +1,141 @@
+InstDrv.dll version 0.2 - Installs or Removes Device Drivers\r
+------------------------------------------------------------\r
+\r
+\r
+The plugin helps you to create NSIS scripts for installing device drivers or\r
+removing them again. It can count installed device instances, create new ones\r
+or delete all supported device. InstDrv works on Windows 2000 or later.\r
+\r
+\r
+\r
+InstDrv::InitDriverSetup devClass drvHWID\r
+Return: result\r
+\r
+To start processing a driver, first call this function. devClass is the GUID\r
+of the device class the driver supports, drvHWID is the device hardware ID. If\r
+you don't know what these terms mean, you may want to take a look at the\r
+Windows DDK. This function returns an empty string on success, otherwise an\r
+error message.\r
+\r
+InitDriverSetup has to be called every time after the plugin dll has been\r
+(re-)loaded, or if you want to switch to a different driver.\r
+\r
+\r
+\r
+InstDrv::CountDevices\r
+Return: number\r
+\r
+This call returns the number of installed and supported devices of the driver.\r
+\r
+\r
+\r
+InstDrv::CreateDevice\r
+Return: result\r
+\r
+To create a new deviced node which the driver has to support, use this\r
+function. You may even call it multiple times for more than one instance. The\r
+return value is the Windows error code (in hex). Use CreateDevice before\r
+installing or updating the driver itself.\r
+\r
+\r
+\r
+InstDrv::InstallDriver infPath\r
+Return: result\r
+        reboot\r
+\r
+InstallDriver installs or updates a device driver as specified in the .inf\r
+setup script. It returns a Windows error code (in hex) and, on success, a flag\r
+signalling if a system reboot is required.\r
+\r
+\r
+\r
+InstDrv::DeleteOemInfFiles\r
+Return: result\r
+        oeminf\r
+        oempnf\r
+\r
+DeleteOemInfFiles tries to clean up the Windows inf directory by deleting the\r
+oemXX.inf and oemXX.pnf files associated with the drivers. It returns a\r
+Windows error code (in hex) and, on success, the names of the deleted files.\r
+This functions requires that at least one device instance is still present.\r
+So, call it before you remove the devices itself. You should also call it\r
+before updating a driver. This avoids that the inf directory gets slowly\r
+messed up with useless old setup scripts (which does NOT really accelerate\r
+Windows). The error code which comes up when no device is installed is\r
+"00000103".\r
+\r
+\r
+\r
+InstDrv::RemoveAllDevices\r
+Return: result\r
+        reboot\r
+\r
+This functions deletes all devices instances the driver supported. It returns\r
+a Windows error code (in hex) and, on success, a flag signalling if the system\r
+needs to be rebooted. You additionally have to remove the driver binaries from\r
+the system paths.\r
+\r
+\r
+\r
+InstDrv::StartSystemService serviceName\r
+Return: result\r
+\r
+Call this function to start the provided system service. The function blocks\r
+until the service is started or the system reported a timeout. The return value\r
+is the Windows error code (in hex).\r
+\r
+\r
+\r
+InstDrv::StopSystemService serviceName\r
+Return: result\r
+\r
+This function tries to stop the provided system service. It blocks until the\r
+service has been shut down or the system reported a timeout. The return value\r
+is the Windows error code (in hex).\r
+\r
+\r
+\r
+Example.nsi\r
+\r
+The example script installs or removes the virtual COM port driver of IrCOMM2k\r
+(2.0.0-alpha8, see www.ircomm2k.de/english). The driver and its setup script\r
+are only included for demonstration purposes, they do not work without the\r
+rest of IrCOMM2k (but they also do not cause any harm).\r
+\r
+\r
+\r
+Building the Source Code\r
+\r
+To build the plugin from the source code, some include files and libraries\r
+which come with the Windows DDK are required.\r
+\r
+\r
+\r
+History\r
+\r
+ 0.2    - fixed bug when calling InitDriverSetup the second time\r
+        - added StartSystemService and StopSystemService\r
+\r
+ 0.1    - first release\r
+\r
+\r
+\r
+License\r
+\r
+Copyright © 2003 Jan Kiszka (Jan.Kiszka@web.de)\r
+\r
+This software is provided 'as-is', without any express or implied\r
+warranty. In no event will the authors be held liable for any damages\r
+arising from the use of this software.\r
+\r
+Permission is granted to anyone to use this software for any purpose,\r
+including commercial applications, and to alter it and redistribute\r
+it freely, subject to the following restrictions:\r
+\r
+1. The origin of this software must not be misrepresented; \r
+   you must not claim that you wrote the original software.\r
+   If you use this software in a product, an acknowledgment in the\r
+   product documentation would be appreciated but is not required.\r
+2. Altered versions must be plainly marked as such,\r
+   and must not be misrepresented as being the original software.\r
+3. This notice may not be removed or altered from any distribution.\r
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.inf
new file mode 100644 (file)
index 0000000..ccda1d8
--- /dev/null
@@ -0,0 +1,137 @@
+; IrCOMM2k.inf\r
+;\r
+; Installation file for the Virtual Infrared-COM-Port\r
+;\r
+; (c) Copyright 2001, 2002 Jan Kiszka \r
+;\r
+\r
+[Version]\r
+Signature="$Windows NT$"\r
+Provider=%JK%\r
+Class=Ports\r
+ClassGUID={4d36e978-e325-11ce-bfc1-08002be10318}\r
+;DriverVer=03/26/2002,1.2.1.0\r
+\r
+[DestinationDirs]\r
+IrCOMM2k.Copy2Drivers  = 12\r
+IrCOMM2k.Copy2Winnt    = 10\r
+IrCOMM2k.Copy2System32 = 11\r
+IrCOMM2k.Copy2Help     = 18\r
+\r
+\r
+;\r
+; Driver information\r
+;\r
+\r
+[Manufacturer]\r
+%JK%   = JK.Mfg\r
+\r
+[JK.Mfg]\r
+%JK.DeviceDescIrCOMM% = IrCOMM2k_inst,IrCOMM2k\r
+\r
+\r
+;\r
+; General installation section\r
+;\r
+\r
+[IrCOMM2k_inst]\r
+CopyFiles = IrCOMM2k.Copy2Drivers ;,IrCOMM2k.Copy2System32,IrCOMM2k.Copy2Help,IrCOMM2k.Copy2Winnt\r
+;AddReg    = IrCOMM2k_inst_AddReg\r
+\r
+\r
+;\r
+; File sections\r
+;\r
+\r
+[IrCOMM2k.Copy2Drivers]\r
+ircomm2k.sys,,,2\r
+\r
+;[IrCOMM2k.Copy2System32]\r
+;ircomm2k.exe,,,2\r
+;ircomm2k.dll,,,2\r
+\r
+;[IrCOMM2k.Copy2Help]\r
+;ircomm2k.hlp,,,2\r
+\r
+;[IrCOMM2k.Copy2Winnt]\r
+;IrCOMM2k-Setup.exe,Setup.exe,,2\r
+\r
+\r
+;\r
+; Service Installation\r
+;\r
+\r
+[IrCOMM2k_inst.Services]\r
+AddService = IrCOMM2k,0x00000002,IrCOMM2k_DriverService_Inst,IrCOMM2k_DriverEventLog_Inst\r
+;AddService = IrCOMM2kSvc,,IrCOMM2k_Service_Inst\r
+\r
+[IrCOMM2k_DriverService_Inst]\r
+DisplayName    = %IrCOMM2k.DrvName%\r
+ServiceType    = 1                  ; SERVICE_KERNEL_DRIVER\r
+StartType      = 3                  ; SERVICE_DEMAND_START\r
+ErrorControl   = 0                  ; SERVICE_ERROR_IGNORE\r
+ServiceBinary  = %12%\ircomm2k.sys\r
+\r
+;[IrCOMM2k_Service_Inst]\r
+;DisplayName    = %IrCOMM2k.SvcName%\r
+;Description    = %IrCOMM2k.SvcDesc%\r
+;ServiceType    = 0x00000120         ; SERVICE_WIN32_SHARE_PROCESS, SERVICE_INTERACTIVE_PROCESS\r
+;StartType      = 2                  ; SERVICE_AUTO_START\r
+;ErrorControl   = 0                  ; SERVICE_ERROR_IGNORE\r
+;ServiceBinary  = %11%\ircomm2k.exe\r
+;Dependencies   = IrCOMM2k\r
+;AddReg         = IrCOMM2kSvcAddReg\r
+\r
+\r
+[IrCOMM2k_inst.nt.HW]\r
+AddReg=IrCOMM2kHwAddReg\r
+\r
+[IrCOMM2kHwAddReg]\r
+HKR,,PortSubClass,REG_BINARY,0x00000001\r
+;HKR,,TimeoutScaling,REG_DWORD,0x00000001\r
+;HKR,,StatusLines,REG_DWORD,0x00000000\r
+\r
+;[IrCOMM2k_inst_AddReg]\r
+;HKR,,EnumPropPages32,,"ircomm2k.dll,IrCOMM2kPropPageProvider"\r
+;HKLM,%UNINSTALL_KEY%,DisplayIcon,0x00020000,"%windir%\IrCOMM2k-Setup.exe"\r
+;HKLM,%UNINSTALL_KEY%,DisplayName,,"IrCOMM2k 1.2.1 "\r
+;HKLM,%UNINSTALL_KEY%,DisplayVersion,,"1.2.1"\r
+;HKLM,%UNINSTALL_KEY%,HelpLink,,"http://www.ircomm2k.de"\r
+;HKLM,%UNINSTALL_KEY%,Publisher,,%JK%\r
+;HKLM,%UNINSTALL_KEY%,UninstallString,0x00020000,"%windir%\IrCOMM2k-Setup.exe"\r
+\r
+;[IrCOMM2kSvcAddReg]\r
+;HKR,Parameters,ActiveConnectOnly,REG_DWORD,0x00000000\r
+\r
+\r
+[IrCOMM2k_DriverEventLog_Inst]\r
+AddReg = IrCOMM2k_DriverEventLog_AddReg\r
+\r
+[IrCOMM2k_DriverEventLog_AddReg]\r
+HKR,,EventMessageFile,REG_EXPAND_SZ,"%SystemRoot%\System32\IoLogMsg.dll;%SystemRoot%\System32\drivers\ircomm2k.sys"\r
+HKR,,TypesSupported,REG_DWORD,7\r
+\r
+\r
+[Strings]\r
+\r
+;\r
+; Non-Localizable Strings\r
+;\r
+\r
+REG_SZ         = 0x00000000\r
+REG_MULTI_SZ   = 0x00010000\r
+REG_EXPAND_SZ  = 0x00020000\r
+REG_BINARY     = 0x00000001\r
+REG_DWORD      = 0x00010001\r
+SERVICEROOT    = "System\CurrentControlSet\Services"\r
+UNINSTALL_KEY  = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\IrCOMM2k"\r
+\r
+;\r
+; Localizable Strings\r
+;\r
+\r
+JK                  = "Jan Kiszka"\r
+JK.DeviceDescIrCOMM = "Virtueller Infrarot-Kommunikationsanschluss"\r
+IrCOMM2k.DrvName    = "Virtueller Infrarot-Kommunikationsanschluss"\r
+;IrCOMM2k.SvcName    = "Virtueller Infrarot-Kommunikationsanschluß, Dienstprogramm"\r
+;IrCOMM2k.SvcDesc    = "Bildet über Infarot einen Kommunikationsanschluß nach."\r
diff --git a/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys
new file mode 100644 (file)
index 0000000..7882583
Binary files /dev/null and b/altosui/Instdrv/NSIS/Contrib/InstDrv/ircomm2k.sys differ
diff --git a/altosui/Instdrv/NSIS/Plugins/InstDrv.dll b/altosui/Instdrv/NSIS/Plugins/InstDrv.dll
new file mode 100644 (file)
index 0000000..482e955
Binary files /dev/null and b/altosui/Instdrv/NSIS/Plugins/InstDrv.dll differ
diff --git a/altosui/Makefile-standalone b/altosui/Makefile-standalone
new file mode 100644 (file)
index 0000000..0d9931d
--- /dev/null
@@ -0,0 +1,184 @@
+.SUFFIXES: .java .class
+
+CLASSPATH=classes:./*:/usr/share/java/*
+CLASSFILES=\
+       Altos.class \
+       AltosChannelMenu.class \
+       AltosConfig.class \
+       AltosConfigUI.class \
+       AltosConvert.class \
+       AltosCRCException.class \
+       AltosCSV.class \
+       AltosCSVUI.class \
+       AltosDebug.class \
+       AltosEepromDownload.class \
+       AltosEepromMonitor.class \
+       AltosEepromReader.class \
+       AltosEepromRecord.class \
+       AltosFile.class \
+       AltosFlash.class \
+       AltosFlashUI.class \
+       AltosFlightInfoTableModel.class \
+       AltosFlightStatusTableModel.class \
+       AltosGPS.class \
+       AltosGreatCircle.class \
+       AltosHexfile.class \
+       AltosLine.class \
+       AltosInfoTable.class \
+       AltosLog.class \
+       AltosLogfileChooser.class \
+       AltosParse.class \
+       AltosPreferences.class \
+       AltosReader.class \
+       AltosRecord.class \
+       AltosSerialMonitor.class \
+       AltosSerial.class \
+       AltosState.class \
+       AltosStatusTable.class \
+       AltosTelemetry.class \
+       AltosTelemetryReader.class \
+       AltosUI.class \
+       AltosDevice.class \
+       AltosDeviceDialog.class \
+       AltosRomconfig.class \
+       AltosRomconfigUI.class \
+       AltosVoice.class
+
+JAVA_ICON=../icon/altus-metrum-16x16.jpg
+WINDOWS_ICON=../icon/altus-metrum.ico
+
+# where altosui.jar gets installed
+ALTOSLIB=/usr/share/java
+
+# where freetts.jar is to be found
+FREETTSLIB=/usr/share/java
+
+# all of the freetts files
+FREETTSJAR= \
+       $(FREETTSLIB)/cmudict04.jar \
+       $(FREETTSLIB)/cmulex.jar \
+       $(FREETTSLIB)/cmu_time_awb.jar \
+       $(FREETTSLIB)/cmutimelex.jar \
+       $(FREETTSLIB)/cmu_us_kal.jar \
+       $(FREETTSLIB)/en_us.jar \
+       $(FREETTSLIB)/freetts.jar
+
+# The current hex files
+HEXLIB=../src
+HEXFILES = \
+       $(HEXLIB)/telemetrum-v1.0.ihx \
+       $(HEXLIB)/teledongle-v0.2.ihx
+
+JAVAFLAGS=-Xlint:unchecked -Xlint:deprecation
+
+ALTOSUIJAR = altosui.jar
+FATJAR = fat/altosui.jar
+
+OS:=$(shell uname)
+
+LINUX_APP=altosui
+
+DARWIN_ZIP=Altos-Mac.zip
+
+WINDOWS_EXE=Altos-Windows.exe
+
+LINUX_TGZ=Altos-Linux.tgz
+
+all: altosui.jar $(LINUX_APP)
+fat: altosui.jar $(LINUX_APP) $(DARWIN_ZIP) $(WINDOWS_EXE) $(LINUX_TGZ)
+
+$(CLASSFILES):
+
+.java.class:
+       javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java
+
+altosui.jar: classes/images classes/altosui classes/libaltosJNI $(CLASSFILES) Manifest.txt
+       cd ./classes && jar cfm ../$@ altosui/Manifest.txt images/* altosui/*.class libaltosJNI/*.class
+
+Manifest.txt: Makefile $(CLASSFILES)
+       echo 'Main-Class: altosui.AltosUI' > $@
+       echo "Class-Path: $(FREETTSLIB)/freetts.jar" >> $@
+
+classes/altosui:
+       mkdir -p classes
+       ln -sf .. classes/altosui
+
+classes/libaltosJNI:
+       mkdir -p classes
+       ln -sf ../libaltos/libaltosJNI classes/libaltosJNI
+
+classes/images:
+       mkdir -p classes/images
+       ln -sf ../$(JAVA_ICON) classes/images
+
+altosui:
+       echo "#!/bin/sh" > $@
+       echo "exec java -Djava.library.path=/usr/lib/altos -jar /usr/share/java/altosui.jar" >> $@
+       chmod +x ./altosui
+
+fat/altosui:
+       echo "#!/bin/sh" > $@
+       echo 'ME=`which "$0"`' >> $@
+       echo 'DIR=`dirname "$ME"`' >> $@
+       echo 'exec java -Djava.library.path="$$DIR" -jar "$$DIR"/altosui.jar' >> $@
+       chmod +x $@
+
+fat/altosui.jar: $(CLASSFILES) $(JAVA_ICON) fat/classes/Manifest.txt
+       mkdir -p fat/classes
+       test -L fat/classes/altosui || ln -sf ../.. fat/classes/altosui
+       mkdir -p fat/classes/images
+       cp $(JAVA_ICON) fat/classes/images
+       test -L fat/classes/libaltosJNI || ln -sf ../../libaltos/libaltosJNI fat/classes/libaltosJNI
+       cd ./fat/classes && jar cfm ../../$@ Manifest.txt images/* altosui/*.class libaltosJNI/*.class
+
+fat/classes/Manifest.txt: $(CLASSFILES) Makefile
+       mkdir -p fat/classes
+       echo 'Main-Class: altosui.AltosUI' > $@
+       echo "Class-Path: freetts.jar" >> $@
+
+install: altosui.jar altosui
+       install -m 0644 altosui.jar $(DESTDIR)/usr/share/java/altosui.jar
+       install -m 0644 altosui.1 $(DESTDIR)/usr/share/man/man1/altosui.1
+       install altosui $(DESTDIR)/usr/bin/altosui
+
+clean:
+       rm -f *.class altosui.jar
+       rm -f AltosUI.app/Contents/Resources/Java/*
+       rm -rf classes
+       rm -rf windows linux
+
+distclean:     clean
+       rm -f $(DARWIN_ZIP) $(WINDOWS_EXE) $(LINUX_TGZ)
+       rm -rf darwin fat
+
+FAT_FILES=$(FATJAR) $(FREETTSJAR) $(HEXFILES)
+
+LINUX_FILES=$(FAT_FILES) libaltos/libaltos.so fat/altosui
+$(LINUX_TGZ): $(LINUX_FILES)
+       rm -f $@
+       mkdir -p linux/AltOS
+       rm -f linux/AltOS/*
+       cp $(LINUX_FILES) linux/AltOS
+       cd linux && tar czf ../$@ AltOS
+
+DARWIN_RESOURCES=$(FATJAR) $(FREETTSJAR) libaltos/libaltos.dylib
+DARWIN_EXTRA=$(HEXFILES)
+DARWIN_FILES=$(DARWIN_RESOURCES) $(DARWIN_EXTRA)
+
+$(DARWIN_ZIP): $(DARWIN_FILES)
+       rm -f $@
+       cp -a AltosUI.app darwin/
+       mkdir -p darwin/AltosUI.app/Contents/Resources/Java
+       cp $(DARWIN_RESOURCES) darwin/AltosUI.app/Contents/Resources/Java
+       mkdir -p darwin/AltOS
+       cp $(DARWIN_EXTRA) darwin/AltOS
+       cd darwin && zip -r ../$@ AltosUI.app AltOS
+
+WINDOWS_FILES = $(FAT_FILES) libaltos/altos.dll ../telemetrum.inf $(WINDOWS_ICON)
+
+$(WINDOWS_EXE): $(WINDOWS_FILES) altos-windows.nsi
+       rm -f $@
+       mkdir -p windows/AltOS
+       rm -f windows/AltOS/*
+       cp $(WINDOWS_FILES) windows/AltOS
+       makensis altos-windows.nsi
diff --git a/altosui/Makefile.am b/altosui/Makefile.am
new file mode 100644 (file)
index 0000000..9f75d5e
--- /dev/null
@@ -0,0 +1,330 @@
+SUBDIRS=libaltos
+JAVAROOT=classes
+AM_JAVACFLAGS=-encoding UTF-8 -Xlint:deprecation
+
+man_MANS=altosui.1
+
+altoslibdir=$(libdir)/altos
+
+CLASSPATH_ENV=mkdir -p $(JAVAROOT); CLASSPATH=".:classes:../altoslib/*:libaltos::$(JFREECHART)/jfreechart.jar:$(FREETTS)/freetts.jar"
+
+bin_SCRIPTS=altosui
+
+altosui_BT = \
+       AltosBTDevice.java \
+       AltosBTDeviceIterator.java \
+       AltosBTManage.java \
+       AltosBTKnown.java
+
+altosui_JAVA = \
+       GrabNDrag.java \
+       AltosAscent.java \
+       AltosChannelMenu.java \
+       AltosCompanionInfo.java \
+       AltosConfig.java \
+       AltosConfigFreqUI.java \
+       AltosConfigUI.java \
+       AltosConfigureUI.java \
+       AltosConfigTD.java \
+       AltosConfigTDUI.java \
+       AltosCSV.java \
+       AltosCSVUI.java \
+       AltosDebug.java \
+       AltosDescent.java \
+       AltosDeviceDialog.java \
+       AltosDevice.java \
+       AltosUSBDevice.java \
+       AltosDisplayThread.java \
+       AltosEepromDelete.java \
+       AltosEepromDownload.java \
+       AltosEepromList.java \
+       AltosEepromManage.java \
+       AltosEepromMonitor.java \
+       AltosEepromSelect.java \
+       AltosFlash.java \
+       AltosFlashUI.java \
+       AltosFlightDisplay.java \
+       AltosFlightInfoTableModel.java \
+       AltosFlightStats.java \
+       AltosFlightStatsTable.java \
+       AltosFlightStatus.java \
+       AltosFlightStatusUpdate.java \
+       AltosFlightUI.java \
+       AltosFontListener.java \
+       AltosFreqList.java \
+       AltosHexfile.java \
+       Altos.java \
+       AltosIdleMonitorUI.java \
+       AltosIgniteUI.java \
+       AltosLaunch.java \
+       AltosLaunchUI.java \
+       AltosInfoTable.java \
+       AltosKML.java \
+       AltosLanded.java \
+       AltosLed.java \
+       AltosLights.java \
+       AltosPad.java \
+       AltosUIPreferences.java \
+       AltosRomconfig.java \
+       AltosRomconfigUI.java \
+       AltosScanUI.java \
+       AltosSerial.java \
+       AltosSerialInUseException.java \
+       AltosSiteMap.java \
+       AltosSiteMapPreload.java \
+       AltosSiteMapCache.java \
+       AltosSiteMapTile.java \
+       AltosUI.java \
+       AltosUIListener.java \
+       AltosFrame.java \
+       AltosDialog.java \
+       AltosWriter.java \
+       AltosDataPointReader.java \
+       AltosDataPoint.java \
+       AltosGraph.java \
+       AltosGraphTime.java \
+       AltosGraphUI.java \
+       AltosDataChooser.java \
+       AltosVersion.java \
+       AltosVoice.java \
+       $(altosui_BT)
+
+JFREECHART_CLASS= \
+    jfreechart.jar
+
+JCOMMON_CLASS=\
+    jcommon.jar
+
+FREETTS_CLASS= \
+       cmudict04.jar \
+       cmulex.jar \
+       cmu_time_awb.jar \
+       cmutimelex.jar \
+       cmu_us_kal.jar \
+       en_us.jar \
+       freetts.jar
+
+ALTOSLIB_CLASS=\
+       AltosLib.jar
+
+LIBALTOS= \
+       libaltos.so \
+       libaltos.dylib \
+       altos.dll
+
+JAR=altosui.jar
+
+FATJAR=altosui-fat.jar
+
+# Icons
+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_0_2=$(top_srcdir)/src/teledongle-v0.2-$(VERSION).ihx
+FIRMWARE_TD=$(FIRMWARE_TD_0_2)
+
+FIRMWARE_TM_1_0=$(top_srcdir)/src/telemetrum-v1.0-$(VERSION).ihx
+FIRMWARE_TM_1_1=$(top_srcdir)/src/telemetrum-v1.1-$(VERSION).ihx
+FIRMWARE_TM_1_2=$(top_srcdir)/src/telemetrum-v1.2-$(VERSION).ihx
+FIRMWARE_TM=$(FIRMWARE_TM_1_0) $(FIRMWARE_TM_1_1) $(FIRMWARE_TM_1_2)
+
+FIRMWARE_TELEMINI_1_0=$(top_srcdir)/src/telemini-v1.0-$(VERSION).ihx
+FIRMWARE_TELEMINI=$(FIRMWARE_TELEMINI_1_0)
+
+FIRMWARE=$(FIRMWARE_TM) $(FIRMWARE_TELEMINI) $(FIRMWARE_TD) 
+
+ALTUSMETRUM_DOC=$(top_srcdir)/doc/altusmetrum.pdf
+ALTOS_DOC=$(top_srcdir)/doc/altos.pdf
+TELEMETRY_DOC=$(top_srcdir)/doc/telemetry.pdf
+TEMPLATE_DOC=$(top_srcdir)/doc/telemetrum-outline.pdf $(top_srcdir)/doc/megametrum-outline.pdf
+
+DOC=$(ALTUSMETRUM_DOC) $(ALTOS_DOC) $(TELEMETRY_DOC) $(TEMPLATE_DOC)
+
+# Distribution targets
+LINUX_DIST=Altos-Linux-$(VERSION).tar.bz2
+MACOSX_DIST=Altos-Mac-$(VERSION).zip
+WINDOWS_DIST=Altos-Windows-$(VERSION_DASH).exe
+
+FAT_FILES=$(FATJAR) $(ALTOSLIB_CLASS) $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS)
+
+LINUX_FILES=$(FAT_FILES) libaltos.so $(FIRMWARE) $(DOC)
+LINUX_EXTRA=altosui-fat
+
+MACOSX_INFO_PLIST=Info.plist
+MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST)
+MACOSX_EXTRA=$(FIRMWARE)
+
+WINDOWS_FILES=$(FAT_FILES) altos.dll altos64.dll $(top_srcdir)/telemetrum.inf $(WINDOWS_ICON)
+
+all-local: classes/altosui $(JAR) altosui altosui-test altosui-jdb
+
+clean-local:
+       -rm -rf classes $(JAR) $(FATJAR) \
+               $(LINUX_DIST) $(MACOSX_DIST) windows $(WINDOWS_DIST) $(ALTOSLIB_CLASS) $(FREETTS_CLASS) \
+               $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) Manifest.txt Manifest-fat.txt altos-windows.log \
+               altosui altosui-test altosui-jdb macosx linux
+
+if FATINSTALL
+
+FATTARGET=$(FATDIR)/$(VERSION)
+
+LINUX_DIST_TARGET=$(FATTARGET)/$(LINUX_DIST)
+MACOSX_DIST_TARGET=$(FATTARGET)/$(MACOSX_DIST)
+WINDOWS_DIST_TARGET=$(FATTARGET)/$(WINDOWS_DIST)
+
+fat: $(LINUX_DIST_TARGET) $(MACOSX_DIST_TARGET) $(WINDOWS_DIST_TARGET)
+
+$(LINUX_DIST_TARGET): $(LINUX_DIST)
+       mkdir -p $(FATTARGET)
+       cp -p $< $@
+
+$(MACOSX_DIST_TARGET): $(MACOSX_DIST)
+       mkdir -p $(FATTARGET)
+       cp -p $< $@
+
+$(WINDOWS_DIST_TARGET): $(WINDOWS_DIST)
+       mkdir -p $(FATTARGET)
+       cp -p $< $@
+
+else
+fat: $(LINUX_DIST) $(MACOSX_DIST) $(WINDOWS_DIST)
+endif
+
+
+altosuidir=$(datadir)/java
+
+install-altosuiJAVA: altosui.jar
+       @$(NORMAL_INSTALL)
+       test -z "$(altosuidir)" || $(MKDIR_P) "$(DESTDIR)$(altosuidir)"
+       echo " $(INSTALL_DATA)" "$<" "'$(DESTDIR)$(altosuidir)/altosui.jar'"; \
+       $(INSTALL_DATA) "$<" "$(DESTDIR)$(altosuidir)"
+
+classes/altosui:
+       mkdir -p classes/altosui
+
+$(JAR): classaltosui.stamp Manifest.txt $(JAVA_ICON) $(ALTOSLIB_CLASS)
+       jar cfm $@ Manifest.txt \
+               $(ICONJAR) \
+               -C classes altosui \
+               -C libaltos libaltosJNI
+
+$(FATJAR): classaltosui.stamp Manifest-fat.txt $(ALTOSLIB_CLASS) $(FREETTS_CLASS) $(JFREECHART_CLASS) $(JCOMMON_CLASS) $(LIBALTOS) $(JAVA_ICON)
+       jar cfm $@ Manifest-fat.txt \
+               $(ICONJAR) \
+               -C classes altosui \
+               -C libaltos libaltosJNI
+
+Manifest.txt: Makefile
+       echo 'Main-Class: altosui.AltosUI' > $@
+       echo "Class-Path: AltosLib.jar $(FREETTS)/freetts.jar $(JFREECHART)/jfreechart.jar $(JCOMMON)/jcommon.jar" >> $@
+
+Manifest-fat.txt:
+       echo 'Main-Class: altosui.AltosUI' > $@
+       echo "Class-Path: AltosLib.jar freetts.jar jfreechart.jar jcommon.jar" >> $@
+
+altosui: Makefile
+       echo "#!/bin/sh" > $@
+       echo 'exec java  -cp "$(FREETTS)/*:$(JFREECHART)/*:$(JCOMMON)/*" -Djava.library.path="$(altoslibdir)" -jar "$(altosuidir)/altosui.jar" "$$@"' >> $@
+       chmod +x $@
+
+altosui-test: Makefile
+       echo "#!/bin/sh" > $@
+       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 "$$@"' >> $@
+       chmod +x $@
+
+libaltos.so: build-libaltos
+       -rm -f "$@"
+       $(LN_S) libaltos/.libs/"$@" .
+
+libaltos.dylib:
+       -rm -f "$@"
+       $(LN_S) libaltos/"$@" .
+
+altos.dll: libaltos/altos.dll
+       -rm -f "$@"
+       $(LN_S) libaltos/"$@" .
+
+altos64.dll: libaltos/altos64.dll
+       -rm -f "$@"
+       $(LN_S) libaltos/"$@" .
+
+libaltos/.libs/libaltos.so: build-libaltos
+
+libaltos/altos.dll: build-altos-dll
+
+libaltos/altos64.dll: build-altos64-dll
+
+build-libaltos:
+       +cd libaltos && make libaltos.la
+build-altos-dll:
+       +cd libaltos && make altos.dll
+
+build-altos64-dll:
+       +cd libaltos && make altos64.dll
+
+$(ALTOSLIB_CLASS):
+       -rm -f "$@"
+       $(LN_S) ../altoslib/"$@" .
+
+$(FREETTS_CLASS):
+       -rm -f "$@"
+       $(LN_S) "$(FREETTS)"/"$@" .
+
+$(JFREECHART_CLASS):
+       -rm -f "$@"
+       $(LN_S) "$(JFREECHART)"/"$@" .
+
+$(JCOMMON_CLASS):
+       -rm -f "$@"
+       $(LN_S) "$(JCOMMON)"/"$@" .
+
+$(LINUX_DIST): $(LINUX_FILES) $(LINUX_EXTRA)
+       -rm -f $@
+       -rm -rf linux
+       mkdir -p linux/AltOS
+       cp -p $(LINUX_FILES) linux/AltOS
+       cp -p altosui-fat linux/AltOS/altosui
+       chmod +x linux/AltOS/altosui
+       tar cjf $@ -C linux AltOS
+
+$(MACOSX_DIST): $(MACOSX_FILES) $(MACOSX_EXTRA)
+       -rm -f $@
+       -rm -rf macosx
+       mkdir macosx
+       cp -a AltosUI.app macosx/
+       cp -p Info.plist macosx/AltosUI.app/Contents
+       mkdir -p macosx/AltOS macosx/AltosUI.app/Contents/Resources/Java
+       cp -p $(FATJAR) macosx/AltosUI.app/Contents/Resources/Java/altosui.jar
+       cp -p libaltos.dylib macosx/AltosUI.app/Contents/Resources/Java
+       cp -p $(ALTOSLIB_CLASS) macosx/AltosUI.app/Contents/Resources/Java
+       cp -p $(FREETTS_CLASS) macosx/AltosUI.app/Contents/Resources/Java
+       cp -p $(JFREECHART_CLASS) macosx/AltosUI.app/Contents/Resources/Java
+       cp -p $(JCOMMON_CLASS) macosx/AltosUI.app/Contents/Resources/Java
+       cp -p $(MACOSX_EXTRA) macosx/AltOS
+       cd macosx && zip -r ../$@ AltosUI.app AltOS
+
+$(WINDOWS_DIST): $(WINDOWS_FILES) altos-windows.nsi
+       -rm -f $@
+       makensis -Oaltos-windows.log "-XOutFile $@" "-DVERSION=$(VERSION)" altos-windows.nsi
+
+publish:
+       scp launch-sites.txt gag.com:public_html
\ No newline at end of file
diff --git a/altosui/altos-windows.nsi b/altosui/altos-windows.nsi
new file mode 100644 (file)
index 0000000..986919d
--- /dev/null
@@ -0,0 +1,168 @@
+!addplugindir Instdrv/NSIS/Plugins
+; Definitions for Java 1.6 Detection
+!define JRE_VERSION "1.6"
+!define JRE_ALTERNATE "1.7"
+!define JRE_URL "http://javadl.sun.com/webapps/download/AutoDL?BundleId=52247&/jre-6u27-windows-i586-p.exe"
+!define PRODUCT_NAME "Altus Metrum Windows Software"
+
+Name "Altus Metrum Installer"
+
+; Default install directory
+InstallDir "$PROGRAMFILES\AltusMetrum"
+
+; Tell the installer where to re-install a new version
+InstallDirRegKey HKLM "Software\AltusMetrum" "Install_Dir"
+
+LicenseText "GNU General Public License Version 2"
+LicenseData "../COPYING"
+
+; Need admin privs for Vista or Win7
+RequestExecutionLevel admin
+
+ShowInstDetails Show
+
+ComponentText "Altus Metrum Software and Driver Installer"
+
+Function GetJRE
+        MessageBox MB_OK "${PRODUCT_NAME} uses Java ${JRE_VERSION} 32-bit, it will now \
+                         be downloaded and installed"
+
+        StrCpy $2 "$TEMP\Java Runtime Environment.exe"
+        nsisdl::download /TIMEOUT=30000 ${JRE_URL} $2
+        Pop $R0 ;Get the return value
+                StrCmp $R0 "success" +3
+                MessageBox MB_OK "Download failed: $R0"
+                Quit
+        ExecWait $2
+        Delete $2
+FunctionEnd
+
+
+Function DetectJRE
+  ReadRegStr $2 HKLM "SOFTWARE\JavaSoft\Java Runtime Environment" \
+             "CurrentVersion"
+  StrCmp $2 ${JRE_VERSION} done
+
+  StrCmp $2 ${JRE_ALTERNATE} done
+
+  Call GetJRE
+
+  done:
+FunctionEnd
+
+; Pages to present
+
+Page license
+Page components
+Page directory
+Page instfiles
+
+UninstPage uninstConfirm
+UninstPage instfiles
+
+; And the stuff to install
+
+Section "Install Driver" InstDriver
+
+       InstDrv::InitDriverSetup /NOUNLOAD {4D36E96D-E325-11CE-BFC1-08002BE10318} AltusMetrumSerial
+       Pop $0
+       DetailPrint "InitDriverSetup: $0"
+       InstDrv::DeleteOemInfFiles /NOUNLOAD
+       InstDrv::CreateDevice /NOUNLOAD
+
+       SetOutPath $TEMP
+       File "../telemetrum.inf"
+       InstDrv::InstallDriver /NOUNLOAD "$TEMP\telemetrum.inf"
+
+       SetOutPath $INSTDIR
+       File "../telemetrum.inf"
+
+       SetOutPath $WINDIR\Inf
+       File "../telemetrum.inf"
+
+SectionEnd
+
+Section "AltosUI Application"
+       Call DetectJRE
+
+       SetOutPath $INSTDIR
+
+       File "altosui-fat.jar"
+       File "cmudict04.jar"
+       File "cmulex.jar"
+       File "cmu_time_awb.jar"
+       File "cmutimelex.jar"
+       File "cmu_us_kal.jar"
+       File "en_us.jar"
+       File "freetts.jar"
+       File "jfreechart.jar"
+       File "jcommon.jar"
+
+       File "*.dll"
+
+       File "../icon/*.ico"
+
+       CreateShortCut "$SMPROGRAMS\AltusMetrum.lnk" "$SYSDIR\javaw.exe" "-jar altosui-fat.jar" "$INSTDIR\altus-metrum.ico"
+SectionEnd
+
+Section "AltosUI Desktop Shortcut"
+       CreateShortCut "$DESKTOP\AltusMetrum.lnk" "$INSTDIR\altosui-fat.jar"  "" "$INSTDIR\altus-metrum.ico"
+SectionEnd
+
+Section "TeleMetrum and TeleDongle Firmware"
+
+       SetOutPath $INSTDIR
+
+       File "../src/telemetrum-v1.0/telemetrum-v1.0-${VERSION}.ihx"
+       File "../src/telemetrum-v1.1/telemetrum-v1.1-${VERSION}.ihx"
+       File "../src/telemetrum-v1.2/telemetrum-v1.2-${VERSION}.ihx"
+       File "../src/telemini-v1.0/telemini-v1.0-${VERSION}.ihx"
+       File "../src/teledongle-v0.2/teledongle-v0.2-${VERSION}.ihx"
+
+SectionEnd
+
+Section "Documentation"
+
+       SetOutPath $INSTDIR
+
+       File "../doc/altusmetrum.pdf"
+       File "../doc/altos.pdf"
+       File "../doc/telemetry.pdf"
+       File "../doc/telemetrum-outline.pdf"
+       File "../doc/megametrum-outline.pdf"
+SectionEnd
+
+Section "Uninstaller"
+
+       ; Deal with the uninstaller
+
+       SetOutPath $INSTDIR
+
+       ; Write the install path to the registry
+       WriteRegStr HKLM SOFTWARE\AltusMetrum "Install_Dir" "$INSTDIR"
+
+       ; Write the uninstall keys for windows
+       WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "DisplayName" "Altus Metrum"
+       WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "UninstallString" '"$INSTDIR\uninstall.exe"'
+       WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "NoModify" "1"
+       WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum" "NoRepair" "1"
+
+       WriteUninstaller "uninstall.exe"
+SectionEnd
+
+Section "Uninstall"
+       DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\AltusMetrum"
+       DeleteRegKey HKLM "Software\AltusMetrum"
+
+       Delete "$INSTDIR\*.*"
+       RMDir "$INSTDIR"
+
+       ; Remove devices
+       InstDrv::InitDriverSetup /NOUNLOAD {4D36E96D-E325-11CE-BFC1-08002BE10318} AltusMetrumSerial
+       InstDrv::DeleteOemInfFiles /NOUNLOAD
+       InstDrv::RemoveAllDevices
+
+       ; Remove shortcuts, if any
+       Delete "$SMPROGRAMS\AltusMetrum.lnk"
+       Delete "$DESKTOP\AltusMetrum.lnk"
+SectionEnd
diff --git a/altosui/altosui-fat b/altosui/altosui-fat
new file mode 100755 (executable)
index 0000000..95b1c05
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+me=`which "$0"`
+dir=`dirname "$me"`
+exec java -cp "$dir/*" -Djava.library.path="$dir" -jar "$dir"/altosui-fat.jar  "$@"
diff --git a/altosui/altosui.1 b/altosui/altosui.1
new file mode 100644 (file)
index 0000000..57fa448
--- /dev/null
@@ -0,0 +1,46 @@
+.\"
+.\" Copyright © 2010 Bdale Garbee <bdale@gag.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH ALTOSUI 1 "altosui" ""
+.SH NAME
+altosui \- Rocket flight monitor
+.SH SYNOPSIS
+.B "altosui"
+.SH DESCRIPTION
+.I altosui
+connects to a TeleDongle or TeleMetrum device through a USB serial device.
+It provides a menu-oriented
+user interface to monitor, record and review rocket flight data.
+.SH USAGE
+When connected to a TeleDongle device, altosui turns on the radio
+receiver and listens for telemetry packets. It displays the received
+telemetry data, and reports flight status via voice synthesis. All
+received telemetry information is recorded to a file.
+.P
+When connected to a TeleMetrum device, altosui can be used to configure the
+TeleMetrum, and to downloads the eeprom data and store it in a file.
+.P
+A number of other menu options exist, including the ability to export flight
+data in different formats.
+.SH FILES
+All data log files are recorded into a user-specified directory
+(default ~/TeleMetrum). Files are named using the current date, the serial
+number of the reporting device, the flight number recorded in the data
+and either '.telem' for telemetry data or '.eeprom' for eeprom data.
+.SH AUTHOR
+Keith Packard
diff --git a/altosui/altusmetrum.jpg b/altosui/altusmetrum.jpg
new file mode 100644 (file)
index 0000000..0402792
Binary files /dev/null and b/altosui/altusmetrum.jpg differ
diff --git a/altosui/launch-sites.txt b/altosui/launch-sites.txt
new file mode 100644 (file)
index 0000000..de7955e
--- /dev/null
@@ -0,0 +1,13 @@
+AHPRA BALLS:40.808333333333333:-119.15
+ARS Rio Rancho:35.33475:-106.75361
+HARA Bragg Farms:34.895875:-86.616211
+KLOUDBusters Rocket Pasture:37.167833333:-97.73975
+METRA Pine Island:41.31939:-74.47077
+NCR Pawnee:40.885955:-104.63793
+OPROC Discovery Bay:47.97808,-122.896383
+OROC Brothers:43.79949786336483:-120.6485810681392
+OROC Sheridan:45.044176:-123.314323
+QRS Cedar Grove:-27.8512283:152.9624000
+SCORE Hudson Ranch:38.155602777777778:-104.809119444444444
+Tripoli Colorado Hartsel:39.009247:-105.702338
+WAC Sportsman Club:47.815327777777778:-119.427186111111111
diff --git a/altosui/libaltos/.gitignore b/altosui/libaltos/.gitignore
new file mode 100644 (file)
index 0000000..c490e6f
--- /dev/null
@@ -0,0 +1,12 @@
+*.so
+*.lo
+*.la
+*.java
+*.class
+.libs/
+classlibaltos.stamp
+libaltos_wrap.c
+libaltosJNI
+cjnitest
+libaltos.swig
+swig_bindings/
diff --git a/altosui/libaltos/Makefile-standalone b/altosui/libaltos/Makefile-standalone
new file mode 100644 (file)
index 0000000..4e43805
--- /dev/null
@@ -0,0 +1,126 @@
+OS:=$(shell uname)
+
+#
+# Linux
+#
+ifeq ($(OS),Linux)
+
+JAVA_CFLAGS=-I/usr/lib/jvm/java-6-openjdk/include
+
+OS_LIB_CFLAGS=-DLINUX -DPOSIX_TTY $(JAVA_CFLAGS)
+
+OS_APP_CFLAGS=$(OS_LIB_CFLAGS)
+
+OS_LDFLAGS=
+
+LIBNAME=libaltos.so
+EXEEXT=
+endif
+
+#
+# Darwin (Mac OS X)
+#
+ifeq ($(OS),Darwin)
+
+OS_LIB_CFLAGS=\
+       -DDARWIN -DPOSIX_TTY -arch i386 -arch x86_64 \
+       --sysroot=/Developer/SDKs/MacOSX10.5.sdk -mmacosx-version-min=10.5 \
+       -iwithsysroot /System/Library/Frameworks/JavaVM.framework/Headers \
+       -iwithsysroot /System/Library/Frameworks/IOKit.framework/Headers \
+       -iwithsysroot /System/Library/Frameworks/CoreFoundation.framework/Headers
+OS_APP_CFLAGS=$(OS_LIB_CFLAGS)
+
+OS_LDFLAGS =\
+       -framework IOKit -framework CoreFoundation
+
+LIBNAME=libaltos.dylib
+EXEEXT=
+
+endif
+
+#
+# Windows
+#
+ifneq (,$(findstring MINGW,$(OS)))
+
+CC=gcc
+
+OS_LIB_CFLAGS = -DWINDOWS -mconsole -DBUILD_DLL
+OS_APP_CFLAGS = -DWINDOWS -mconsole
+
+OS_LDFLAGS = -lgdi32 -luser32 -lcfgmgr32 -lsetupapi -lole32 \
+       -ladvapi32 -lcomctl32 -mconsole -Wl,--add-stdcall-alias
+
+LIBNAME=altos.dll
+
+EXEEXT=.exe
+
+endif
+
+.SUFFIXES: .java .class
+
+CLASSPATH=".:jnitest/*:libaltosJNI:/usr/share/java/*"
+
+SWIG_DIR=swig_bindings/java
+SWIG_FILE=$(SWIG_DIR)/libaltos.swig
+SWIG_WRAP=$(SWIG_DIR)/libaltos_wrap.c
+
+JNI_DIR=libaltosJNI
+JNI_FILE=$(JNI_DIR)/libaltosJNI.java
+JNI_SRCS=$(JNI_FILE) \
+       $(JNI_DIR)/SWIGTYPE_p_altos_file.java \
+       $(JNI_DIR)/SWIGTYPE_p_altos_list.java \
+       $(JNI_DIR)/altos_device.java \
+       $(JNI_DIR)/libaltos.java
+
+JAVAFILES=\
+       $(JNI_SRCS)
+
+CLASSFILES = $(JAVAFILES:%.java=%.class)
+
+JAVAFLAGS=-Xlint:unchecked
+
+CJNITEST=cjnitest$(EXEEXT)
+
+all: $(LIBNAME) $(CJNITEST) $(CLASSFILES)
+
+.java.class:
+       javac -encoding UTF8 -classpath "$(CLASSPATH)" $(JAVAFLAGS) $*.java
+
+CFLAGS=$(OS_LIB_CFLAGS) -O -I.
+
+LDFLAGS=$(OS_LDFLAGS)
+
+HEADERS=libaltos.h
+SRCS = libaltos.c $(SWIG_WRAP)
+OBJS = $(SRCS:%.c=%.o)
+LIBS = $(DARWIN_LIBS)
+
+$(CJNITEST): cjnitest.c $(LIBNAME)
+       $(CC) -o $@ $(OS_APP_CFLAGS) cjnitest.c $(LIBNAME) $(LIBS) $(LDFLAGS)
+
+$(LIBNAME): $(OBJS)
+       $(CC) -shared $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(LDFLAGS)
+
+clean:
+       rm -f $(CLASSFILES) $(OBJS) $(LIBNAME) $(CJNITEST) cjnitest.o
+       rm -rf swig_bindings libaltosJNI
+
+distclean:     clean
+
+$(JNI_FILE): libaltos.i0 $(HEADERS)
+       mkdir -p $(SWIG_DIR)
+       mkdir -p libaltosJNI
+       sed 's;//%;%;' libaltos.i0 $(HEADERS) > $(SWIG_FILE)
+       swig -java -package libaltosJNI $(SWIG_FILE)
+       cp swig_bindings/java/*.java libaltosJNI
+
+$(SWIG_WRAP): $(JNI_FILE)
+
+ifeq ($(OS),Linux)
+install:       $(LIBNAME)
+       install -c $(LIBNAME) $(DESTDIR)/usr/lib/altos/$(LIBNAME)
+
+endif
+
+.NOTPARALLEL:
diff --git a/altosui/libaltos/Makefile.am b/altosui/libaltos/Makefile.am
new file mode 100644 (file)
index 0000000..b5ab1dd
--- /dev/null
@@ -0,0 +1,54 @@
+JAVAC=javac
+AM_CFLAGS=-DLINUX -DPOSIX_TTY -I$(JVM_INCLUDE)
+AM_JAVACFLAGS=-encoding UTF-8
+
+altoslibdir=$(libdir)/altos
+
+altoslib_LTLIBRARIES=libaltos.la
+
+libaltos_la_LDFLAGS = -version-info 1:0:1
+
+libaltos_la_SOURCES=\
+       libaltos.c \
+       libaltos_wrap.c
+
+noinst_PROGRAMS=cjnitest
+
+cjnitest_LDADD=libaltos.la
+
+LIBS=-lbluetooth
+
+HFILES=libaltos.h
+
+SWIG_FILE=libaltos.swig
+
+CLASSDIR=libaltosJNI
+
+$(SWIG_FILE): libaltos.i0 $(HFILES)
+       sed 's;//%;%;' libaltos.i0 $(HFILES) > $(SWIG_FILE)
+
+all-local: classlibaltos.stamp
+
+libaltos_wrap.c: classlibaltos.stamp
+
+classlibaltos.stamp: $(SWIG_FILE)
+       swig -java -package libaltosJNI $(SWIG_FILE)
+       mkdir -p libaltosJNI
+       $(JAVAC) -d . $(AM_JAVACFLAGS) $(JAVACFLAGS) *.java && \
+       touch classlibaltos.stamp
+
+MINGCC32=i686-w64-mingw32-gcc
+MINGCC64=x86_64-w64-mingw32-gcc
+MINGFLAGS=-Wall -DWINDOWS -DBUILD_DLL -I$(JVM_INCLUDE)
+MINGLIBS=-lsetupapi
+
+fat: altos.dll altos64.dll
+
+altos.dll: $(libaltos_la_SOURCES)
+       $(MINGCC32) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS)
+
+altos64.dll: $(libaltos_la_SOURCES)
+       $(MINGCC64) -o $@ $(MINGFLAGS) -shared $(libaltos_la_SOURCES) $(MINGLIBS)
+
+clean-local:
+       -rm -rf libaltosJNI *.class *.java classlibaltos.stamp $(SWIG_FILE) libaltos_wrap.c altos.dll altos64.dll
diff --git a/altosui/libaltos/cjnitest.c b/altosui/libaltos/cjnitest.c
new file mode 100644 (file)
index 0000000..f0fe78f
--- /dev/null
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include "libaltos.h"
+
+static void
+altos_puts(struct altos_file *file, char *string)
+{
+       char    c;
+
+       while ((c = *string++))
+               altos_putchar(file, c);
+}
+
+main ()
+{
+       struct altos_device     device;
+       struct altos_list       *list;
+       struct altos_bt_device  bt_device;
+       struct altos_bt_list    *bt_list;
+
+       altos_init();
+       list = altos_list_start();
+       while (altos_list_next(list, &device)) {
+               struct altos_file       *file;
+               int                     c;
+
+               printf ("%04x:%04x %-20s %4d %s\n", device.vendor, device.product,
+                       device.name, device.serial, device.path);
+
+               file = altos_open(&device);
+               if (!file) {
+                       printf("altos_open failed\n");
+                       continue;
+               }
+               altos_puts(file,"v\nc s\n");
+               altos_flush(file);
+               while ((c = altos_getchar(file, 100)) >= 0) {
+                       putchar (c);
+               }
+               if (c != LIBALTOS_TIMEOUT)
+                       printf ("getchar returns %d\n", c);
+               altos_close(file);
+       }
+       altos_list_finish(list);
+#if HAS_BLUETOOTH
+       bt_list = altos_bt_list_start(8);
+       while (altos_bt_list_next(bt_list, &bt_device)) {
+               printf ("%s %s\n", bt_device.name, bt_device.addr);
+               if (strncmp(bt_device.name, "TeleBT", 6) == 0) {
+                       struct altos_file       *file;
+
+                       int                     c;
+                       file = altos_bt_open(&bt_device);
+                       if (!file) {
+                               printf("altos_bt_open failed\n");
+                               continue;
+                       }
+                       altos_puts(file,"v\nc s\n");
+                       altos_flush(file);
+                       while ((c = altos_getchar(file, 100)) >= 0) {
+                               putchar(c);
+                       }
+                       if (c != LIBALTOS_TIMEOUT)
+                               printf("getchar returns %d\n", c);
+                       altos_close(file);
+               }
+       }
+       altos_bt_list_finish(bt_list);
+#endif
+       altos_fini();
+       return 0;
+}
diff --git a/altosui/libaltos/libaltos.c b/altosui/libaltos/libaltos.c
new file mode 100644 (file)
index 0000000..515432f
--- /dev/null
@@ -0,0 +1,1318 @@
+/*
+ * 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.
+ */
+
+#include "libaltos.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define USB_VENDOR_FSF                 0xfffe
+#define USB_VENDOR_ALTUSMETRUM         USB_VENDOR_FSF
+#define USB_PRODUCT_ALTUSMETRUM                0x000a
+#define USB_PRODUCT_ALTUSMETRUM_MIN    0x000a
+#define USB_PRODUCT_ALTUSMETRUM_MAX    0x00ff
+
+#define USB_IS_ALTUSMETRUM(v,p)        ((v) == USB_VENDOR_ALTUSMETRUM && \
+               (USB_PRODUCT_ALTUSMETRUM_MIN <= (p) && \
+                (p) <= USB_PRODUCT_ALTUSMETRUM_MAX))
+
+#define BLUETOOTH_PRODUCT_TELEBT       "TeleBT"
+
+#define USE_POLL
+
+PUBLIC int
+altos_init(void)
+{
+       return LIBALTOS_SUCCESS;
+}
+
+PUBLIC void
+altos_fini(void)
+{
+}
+
+static struct altos_error last_error;
+
+static void
+altos_set_last_error(int code, char *string)
+{
+       last_error.code = code;
+       strncpy(last_error.string, string, sizeof (last_error.string) -1);
+       last_error.string[sizeof(last_error.string)-1] = '\0';
+}
+
+PUBLIC void
+altos_get_last_error(struct altos_error *error)
+{
+       *error = last_error;
+}
+
+#ifdef DARWIN
+
+#undef USE_POLL
+
+/* Mac OS X don't have strndup even if _GNU_SOURCE is defined */
+static char *
+altos_strndup (const char *s, size_t n)
+{
+    size_t len = strlen (s);
+    char *ret;
+
+    if (len <= n)
+       return strdup (s);
+    ret = malloc(n + 1);
+    strncpy(ret, s, n);
+    ret[n] = '\0';
+    return ret;
+}
+
+#else
+#define altos_strndup strndup
+#endif
+
+#ifdef POSIX_TTY
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <errno.h>
+
+#define USB_BUF_SIZE   64
+
+struct altos_file {
+       int                             fd;
+#ifdef USE_POLL
+       int                             pipe[2];
+#else
+       int                             out_fd;
+#endif
+       unsigned char                   out_data[USB_BUF_SIZE];
+       int                             out_used;
+       unsigned char                   in_data[USB_BUF_SIZE];
+       int                             in_used;
+       int                             in_read;
+};
+
+static void
+altos_set_last_posix_error(void)
+{
+       altos_set_last_error(errno, strerror(errno));
+}
+
+PUBLIC struct altos_file *
+altos_open(struct altos_device *device)
+{
+       struct altos_file       *file = calloc (sizeof (struct altos_file), 1);
+       int                     ret;
+       struct termios          term;
+
+       if (!file) {
+               altos_set_last_posix_error();
+               return NULL;
+       }
+
+//     altos_set_last_error(12, "yeah yeah, failed again");
+//     free(file);
+//     return NULL;
+
+       file->fd = open(device->path, O_RDWR | O_NOCTTY);
+       if (file->fd < 0) {
+               altos_set_last_posix_error();
+               free(file);
+               return NULL;
+       }
+#ifdef USE_POLL
+       pipe(file->pipe);
+#else
+       file->out_fd = open(device->path, O_RDWR | O_NOCTTY);
+       if (file->out_fd < 0) {
+               altos_set_last_posix_error();
+               close(file->fd);
+               free(file);
+               return NULL;
+       }
+#endif
+       ret = tcgetattr(file->fd, &term);
+       if (ret < 0) {
+               altos_set_last_posix_error();
+               close(file->fd);
+#ifndef USE_POLL
+               close(file->out_fd);
+#endif
+               free(file);
+               return NULL;
+       }
+       cfmakeraw(&term);
+#ifdef USE_POLL
+       term.c_cc[VMIN] = 1;
+       term.c_cc[VTIME] = 0;
+#else
+       term.c_cc[VMIN] = 0;
+       term.c_cc[VTIME] = 1;
+#endif
+       ret = tcsetattr(file->fd, TCSAFLUSH, &term);
+       if (ret < 0) {
+               altos_set_last_posix_error();
+               close(file->fd);
+#ifndef USE_POLL
+               close(file->out_fd);
+#endif
+               free(file);
+               return NULL;
+       }
+       return file;
+}
+
+PUBLIC void
+altos_close(struct altos_file *file)
+{
+       if (file->fd != -1) {
+               int     fd = file->fd;
+               file->fd = -1;
+#ifdef USE_POLL
+               write(file->pipe[1], "\r", 1);
+#else
+               close(file->out_fd);
+               file->out_fd = -1;
+#endif
+               close(fd);
+       }
+}
+
+PUBLIC void
+altos_free(struct altos_file *file)
+{
+       altos_close(file);
+       free(file);
+}
+
+PUBLIC int
+altos_flush(struct altos_file *file)
+{
+       if (file->out_used && 0) {
+               printf ("flush \"");
+               fwrite(file->out_data, 1, file->out_used, stdout);
+               printf ("\"\n");
+       }
+       while (file->out_used) {
+               int     ret;
+
+               if (file->fd < 0)
+                       return -EBADF;
+#ifdef USE_POLL
+               ret = write (file->fd, file->out_data, file->out_used);
+#else
+               ret = write (file->out_fd, file->out_data, file->out_used);
+#endif
+               if (ret < 0) {
+                       altos_set_last_posix_error();
+                       return -last_error.code;
+               }
+               if (ret) {
+                       memmove(file->out_data, file->out_data + ret,
+                               file->out_used - ret);
+                       file->out_used -= ret;
+               }
+       }
+       return 0;
+}
+
+PUBLIC int
+altos_putchar(struct altos_file *file, char c)
+{
+       int     ret;
+
+       if (file->out_used == USB_BUF_SIZE) {
+               ret = altos_flush(file);
+               if (ret) {
+                       return ret;
+               }
+       }
+       file->out_data[file->out_used++] = c;
+       ret = 0;
+       if (file->out_used == USB_BUF_SIZE)
+               ret = altos_flush(file);
+       return ret;
+}
+
+#ifdef USE_POLL
+#include <poll.h>
+#endif
+
+static int
+altos_fill(struct altos_file *file, int timeout)
+{
+       int             ret;
+#ifdef USE_POLL
+       struct pollfd   fd[2];
+#endif
+
+       if (timeout == 0)
+               timeout = -1;
+       while (file->in_read == file->in_used) {
+               if (file->fd < 0)
+                       return LIBALTOS_ERROR;
+#ifdef USE_POLL
+               fd[0].fd = file->fd;
+               fd[0].events = POLLIN|POLLERR|POLLHUP|POLLNVAL;
+               fd[1].fd = file->pipe[0];
+               fd[1].events = POLLIN;
+               ret = poll(fd, 2, timeout);
+               if (ret < 0) {
+                       altos_set_last_posix_error();
+                       return LIBALTOS_ERROR;
+               }
+               if (ret == 0)
+                       return LIBALTOS_TIMEOUT;
+
+               if (fd[0].revents & (POLLHUP|POLLERR|POLLNVAL))
+                       return LIBALTOS_ERROR;
+               if (fd[0].revents & POLLIN)
+#endif
+               {
+                       ret = read(file->fd, file->in_data, USB_BUF_SIZE);
+                       if (ret < 0) {
+                               altos_set_last_posix_error();
+                               return LIBALTOS_ERROR;
+                       }
+                       file->in_read = 0;
+                       file->in_used = ret;
+#ifndef USE_POLL
+                       if (ret == 0 && timeout > 0)
+                               return LIBALTOS_TIMEOUT;
+#endif
+               }
+       }
+       if (file->in_used && 0) {
+               printf ("fill \"");
+               fwrite(file->in_data, 1, file->in_used, stdout);
+               printf ("\"\n");
+       }
+       return 0;
+}
+
+PUBLIC int
+altos_getchar(struct altos_file *file, int timeout)
+{
+       int     ret;
+       while (file->in_read == file->in_used) {
+               if (file->fd < 0)
+                       return LIBALTOS_ERROR;
+               ret = altos_fill(file, timeout);
+               if (ret)
+                       return ret;
+       }
+       return file->in_data[file->in_read++];
+}
+
+#endif /* POSIX_TTY */
+
+/*
+ * Scan for Altus Metrum devices by looking through /sys
+ */
+
+#ifdef LINUX
+
+#define _GNU_SOURCE
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+
+static char *
+cc_fullname (char *dir, char *file)
+{
+       char    *new;
+       int     dlen = strlen (dir);
+       int     flen = strlen (file);
+       int     slen = 0;
+
+       if (dir[dlen-1] != '/')
+               slen = 1;
+       new = malloc (dlen + slen + flen + 1);
+       if (!new)
+               return 0;
+       strcpy(new, dir);
+       if (slen)
+               strcat (new, "/");
+       strcat(new, file);
+       return new;
+}
+
+static char *
+cc_basename(char *file)
+{
+       char *b;
+
+       b = strrchr(file, '/');
+       if (!b)
+               return file;
+       return b + 1;
+}
+
+static char *
+load_string(char *dir, char *file)
+{
+       char    *full = cc_fullname(dir, file);
+       char    line[4096];
+       char    *r;
+       FILE    *f;
+       int     rlen;
+
+       f = fopen(full, "r");
+       free(full);
+       if (!f)
+               return NULL;
+       r = fgets(line, sizeof (line), f);
+       fclose(f);
+       if (!r)
+               return NULL;
+       rlen = strlen(r);
+       if (r[rlen-1] == '\n')
+               r[rlen-1] = '\0';
+       return strdup(r);
+}
+
+static int
+load_hex(char *dir, char *file)
+{
+       char    *line;
+       char    *end;
+       long    i;
+
+       line = load_string(dir, file);
+       if (!line)
+               return -1;
+       i = strtol(line, &end, 16);
+       free(line);
+       if (end == line)
+               return -1;
+       return i;
+}
+
+static int
+load_dec(char *dir, char *file)
+{
+       char    *line;
+       char    *end;
+       long    i;
+
+       line = load_string(dir, file);
+       if (!line)
+               return -1;
+       i = strtol(line, &end, 10);
+       free(line);
+       if (end == line)
+               return -1;
+       return i;
+}
+
+static int
+dir_filter_tty_colon(const struct dirent *d)
+{
+       return strncmp(d->d_name, "tty:", 4) == 0;
+}
+
+static int
+dir_filter_tty(const struct dirent *d)
+{
+       return strncmp(d->d_name, "tty", 3) == 0;
+}
+
+struct altos_usbdev {
+       char    *sys;
+       char    *tty;
+       char    *manufacturer;
+       char    *product_name;
+       int     serial; /* AltOS always uses simple integer serial numbers */
+       int     idProduct;
+       int     idVendor;
+};
+
+static char *
+usb_tty(char *sys)
+{
+       char *base;
+       int num_configs;
+       int config;
+       struct dirent **namelist;
+       int interface;
+       int num_interfaces;
+       char endpoint_base[20];
+       char *endpoint_full;
+       char *tty_dir;
+       int ntty;
+       char *tty;
+
+       base = cc_basename(sys);
+       num_configs = load_hex(sys, "bNumConfigurations");
+       num_interfaces = load_hex(sys, "bNumInterfaces");
+       for (config = 1; config <= num_configs; config++) {
+               for (interface = 0; interface < num_interfaces; interface++) {
+                       sprintf(endpoint_base, "%s:%d.%d",
+                               base, config, interface);
+                       endpoint_full = cc_fullname(sys, endpoint_base);
+
+                       /* Check for tty:ttyACMx style names
+                        */
+                       ntty = scandir(endpoint_full, &namelist,
+                                      dir_filter_tty_colon,
+                                      alphasort);
+                       if (ntty > 0) {
+                               free(endpoint_full);
+                               tty = cc_fullname("/dev", namelist[0]->d_name + 4);
+                               free(namelist);
+                               return tty;
+                       }
+
+                       /* Check for tty/ttyACMx style names
+                        */
+                       tty_dir = cc_fullname(endpoint_full, "tty");
+                       free(endpoint_full);
+                       ntty = scandir(tty_dir, &namelist,
+                                      dir_filter_tty,
+                                      alphasort);
+                       free (tty_dir);
+                       if (ntty > 0) {
+                               tty = cc_fullname("/dev", namelist[0]->d_name);
+                               free(namelist);
+                               return tty;
+                       }
+               }
+       }
+       return NULL;
+}
+
+static struct altos_usbdev *
+usb_scan_device(char *sys)
+{
+       struct altos_usbdev *usbdev;
+
+       usbdev = calloc(1, sizeof (struct altos_usbdev));
+       if (!usbdev)
+               return NULL;
+       usbdev->sys = strdup(sys);
+       usbdev->manufacturer = load_string(sys, "manufacturer");
+       usbdev->product_name = load_string(sys, "product");
+       usbdev->serial = load_dec(sys, "serial");
+       usbdev->idProduct = load_hex(sys, "idProduct");
+       usbdev->idVendor = load_hex(sys, "idVendor");
+       usbdev->tty = usb_tty(sys);
+       return usbdev;
+}
+
+static void
+usbdev_free(struct altos_usbdev *usbdev)
+{
+       free(usbdev->sys);
+       free(usbdev->manufacturer);
+       free(usbdev->product_name);
+       /* this can get used as a return value */
+       if (usbdev->tty)
+               free(usbdev->tty);
+       free(usbdev);
+}
+
+#define USB_DEVICES    "/sys/bus/usb/devices"
+
+static int
+dir_filter_dev(const struct dirent *d)
+{
+       const char      *n = d->d_name;
+       char    c;
+
+       while ((c = *n++)) {
+               if (isdigit(c))
+                       continue;
+               if (c == '-')
+                       continue;
+               if (c == '.' && n != d->d_name + 1)
+                       continue;
+               return 0;
+       }
+       return 1;
+}
+
+struct altos_list {
+       struct altos_usbdev     **dev;
+       int                     current;
+       int                     ndev;
+};
+
+struct altos_list *
+altos_list_start(void)
+{
+       int                     e;
+       struct dirent           **ents;
+       char                    *dir;
+       struct altos_usbdev     *dev;
+       struct altos_list       *devs;
+       int                     n;
+
+       devs = calloc(1, sizeof (struct altos_list));
+       if (!devs)
+               return NULL;
+
+       n = scandir (USB_DEVICES, &ents,
+                    dir_filter_dev,
+                    alphasort);
+       if (!n)
+               return 0;
+       for (e = 0; e < n; e++) {
+               dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
+               dev = usb_scan_device(dir);
+               free(dir);
+               if (USB_IS_ALTUSMETRUM(dev->idVendor, dev->idProduct)) {
+                       if (devs->dev)
+                               devs->dev = realloc(devs->dev,
+                                                   (devs->ndev + 1) * sizeof (struct usbdev *));
+                       else
+                               devs->dev = malloc (sizeof (struct usbdev *));
+                       devs->dev[devs->ndev++] = dev;
+               }
+       }
+       free(ents);
+       devs->current = 0;
+       return devs;
+}
+
+int
+altos_list_next(struct altos_list *list, struct altos_device *device)
+{
+       struct altos_usbdev *dev;
+       if (list->current >= list->ndev)
+               return 0;
+       dev = list->dev[list->current];
+       strcpy(device->name, dev->product_name);
+       device->vendor = dev->idVendor;
+       device->product = dev->idProduct;
+       strcpy(device->path, dev->tty);
+       device->serial = dev->serial;
+       list->current++;
+       return 1;
+}
+
+void
+altos_list_finish(struct altos_list *usbdevs)
+{
+       int     i;
+
+       if (!usbdevs)
+               return;
+       for (i = 0; i < usbdevs->ndev; i++)
+               usbdev_free(usbdevs->dev[i]);
+       free(usbdevs);
+}
+
+struct altos_bt_list {
+       inquiry_info    *ii;
+       int             sock;
+       int             dev_id;
+       int             rsp;
+       int             num_rsp;
+};
+
+#define INQUIRY_MAX_RSP        255
+
+struct altos_bt_list *
+altos_bt_list_start(int inquiry_time)
+{
+       struct altos_bt_list    *bt_list;
+
+       bt_list = calloc(1, sizeof (struct altos_bt_list));
+       if (!bt_list)
+               goto no_bt_list;
+
+       bt_list->ii = calloc(INQUIRY_MAX_RSP, sizeof (inquiry_info));
+       if (!bt_list->ii)
+               goto no_ii;
+       bt_list->dev_id = hci_get_route(NULL);
+       if (bt_list->dev_id < 0)
+               goto no_dev_id;
+
+       bt_list->sock = hci_open_dev(bt_list->dev_id);
+       if (bt_list->sock < 0)
+               goto no_sock;
+
+       bt_list->num_rsp = hci_inquiry(bt_list->dev_id,
+                                      inquiry_time,
+                                      INQUIRY_MAX_RSP,
+                                      NULL,
+                                      &bt_list->ii,
+                                      IREQ_CACHE_FLUSH);
+       if (bt_list->num_rsp < 0)
+               goto no_rsp;
+
+       bt_list->rsp = 0;
+       return bt_list;
+
+no_rsp:
+       close(bt_list->sock);
+no_sock:
+no_dev_id:
+       free(bt_list->ii);
+no_ii:
+       free(bt_list);
+no_bt_list:
+       return NULL;
+}
+
+int
+altos_bt_list_next(struct altos_bt_list *bt_list,
+                  struct altos_bt_device *device)
+{
+       inquiry_info    *ii;
+
+       if (bt_list->rsp >= bt_list->num_rsp)
+               return 0;
+
+       ii = &bt_list->ii[bt_list->rsp];
+       ba2str(&ii->bdaddr, device->addr);
+       memset(&device->name, '\0', sizeof (device->name));
+       if (hci_read_remote_name(bt_list->sock, &ii->bdaddr,
+                                sizeof (device->name),
+                                device->name, 0) < 0) {
+               strcpy(device->name, "[unknown]");
+       }
+       bt_list->rsp++;
+       return 1;
+}
+
+void
+altos_bt_list_finish(struct altos_bt_list *bt_list)
+{
+       close(bt_list->sock);
+       free(bt_list->ii);
+       free(bt_list);
+}
+
+void
+altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
+{
+       strncpy(device->name, name, sizeof (device->name));
+       device->name[sizeof(device->name)-1] = '\0';
+       strncpy(device->addr, addr, sizeof (device->addr));
+       device->addr[sizeof(device->addr)-1] = '\0';
+}
+
+struct altos_file *
+altos_bt_open(struct altos_bt_device *device)
+{
+       struct sockaddr_rc addr = { 0 };
+       int     s, status;
+       struct altos_file *file;
+
+       file = calloc(1, sizeof (struct altos_file));
+       if (!file)
+               goto no_file;
+       file->fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+       if (file->fd < 0) {
+               altos_set_last_posix_error();
+               goto no_sock;
+       }
+
+       addr.rc_family = AF_BLUETOOTH;
+       addr.rc_channel = 1;
+       str2ba(device->addr, &addr.rc_bdaddr);
+
+       status = connect(file->fd,
+                        (struct sockaddr *)&addr,
+                        sizeof(addr));
+       if (status < 0) {
+               altos_set_last_posix_error();
+               goto no_link;
+       }
+       sleep(1);
+
+#ifdef USE_POLL
+       pipe(file->pipe);
+#else
+       file->out_fd = dup(file->fd);
+#endif
+       return file;
+no_link:
+       close(s);
+no_sock:
+       free(file);
+no_file:
+       return NULL;
+}
+
+#endif
+
+#ifdef DARWIN
+
+#include <IOKitLib.h>
+#include <IOKit/usb/USBspec.h>
+#include <sys/param.h>
+#include <paths.h>
+#include <CFNumber.h>
+#include <IOBSD.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct altos_list {
+       io_iterator_t iterator;
+};
+
+static int
+get_string(io_object_t object, CFStringRef entry, char *result, int result_len)
+{
+       CFTypeRef entry_as_string;
+       Boolean got_string;
+
+       entry_as_string = IORegistryEntrySearchCFProperty (object,
+                                                          kIOServicePlane,
+                                                          entry,
+                                                          kCFAllocatorDefault,
+                                                          kIORegistryIterateRecursively);
+       if (entry_as_string) {
+               got_string = CFStringGetCString(entry_as_string,
+                                               result, result_len,
+                                               kCFStringEncodingASCII);
+    
+               CFRelease(entry_as_string);
+               if (got_string)
+                       return 1;
+       }
+       return 0;
+}
+
+static int
+get_number(io_object_t object, CFStringRef entry, int *result)
+{
+       CFTypeRef entry_as_number;
+       Boolean got_number;
+       
+       entry_as_number = IORegistryEntrySearchCFProperty (object,
+                                                          kIOServicePlane,
+                                                          entry,
+                                                          kCFAllocatorDefault,
+                                                          kIORegistryIterateRecursively);
+       if (entry_as_number) {
+               got_number = CFNumberGetValue(entry_as_number,
+                                             kCFNumberIntType,
+                                             result);
+               if (got_number)
+                       return 1;
+       }
+       return 0;
+}
+
+PUBLIC struct altos_list *
+altos_list_start(void)
+{
+       struct altos_list *list = calloc (sizeof (struct altos_list), 1);
+       CFMutableDictionaryRef matching_dictionary = IOServiceMatching("IOUSBDevice");
+       io_iterator_t tdIterator;
+       io_object_t tdObject;
+       kern_return_t ret;
+       int i;
+
+       ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dictionary, &list->iterator);
+       if (ret != kIOReturnSuccess)
+               return NULL;
+       return list;
+}
+
+PUBLIC int
+altos_list_next(struct altos_list *list, struct altos_device *device)
+{
+       io_object_t object;
+       char serial_string[128];
+
+       for (;;) {
+               object = IOIteratorNext(list->iterator);
+               if (!object)
+                       return 0;
+  
+               if (!get_number (object, CFSTR(kUSBVendorID), &device->vendor) ||
+                   !get_number (object, CFSTR(kUSBProductID), &device->product))
+                       continue;
+               if (device->vendor != 0xfffe)
+                       continue;
+               if (device->product < 0x000a || 0x0013 < device->product)
+                       continue;
+               if (get_string (object, CFSTR("IOCalloutDevice"), device->path, sizeof (device->path)) &&
+                   get_string (object, CFSTR("USB Product Name"), device->name, sizeof (device->name)) &&
+                   get_string (object, CFSTR("USB Serial Number"), serial_string, sizeof (serial_string))) {
+                       device->serial = atoi(serial_string);
+                       return 1;
+               }
+       }
+}
+
+PUBLIC void
+altos_list_finish(struct altos_list *list)
+{
+       IOObjectRelease (list->iterator);
+       free(list);
+}
+
+struct altos_bt_list {
+       int             sock;
+       int             dev_id;
+       int             rsp;
+       int             num_rsp;
+};
+
+#define INQUIRY_MAX_RSP        255
+
+struct altos_bt_list *
+altos_bt_list_start(int inquiry_time)
+{
+       return NULL;
+}
+
+int
+altos_bt_list_next(struct altos_bt_list *bt_list,
+                  struct altos_bt_device *device)
+{
+       return 0;
+}
+
+void
+altos_bt_list_finish(struct altos_bt_list *bt_list)
+{
+}
+
+void
+altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
+{
+       strncpy(device->name, name, sizeof (device->name));
+       device->name[sizeof(device->name)-1] = '\0';
+       strncpy(device->addr, addr, sizeof (device->addr));
+       device->addr[sizeof(device->addr)-1] = '\0';
+}
+
+struct altos_file *
+altos_bt_open(struct altos_bt_device *device)
+{
+       return NULL;
+}
+
+#endif
+
+
+#ifdef WINDOWS
+
+#include <stdlib.h>
+#include <windows.h>
+#include <setupapi.h>
+
+struct altos_list {
+       HDEVINFO        dev_info;
+       int             index;
+};
+
+#define USB_BUF_SIZE   64
+
+struct altos_file {
+       HANDLE                          handle;
+       unsigned char                   out_data[USB_BUF_SIZE];
+       int                             out_used;
+       unsigned char                   in_data[USB_BUF_SIZE];
+       int                             in_used;
+       int                             in_read;
+       OVERLAPPED                      ov_read;
+       BOOL                            pend_read;
+       OVERLAPPED                      ov_write;
+};
+
+static void
+altos_set_last_windows_error(void)
+{
+       DWORD   error = GetLastError();
+       TCHAR   message[1024];
+       FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+                     0,
+                     error,
+                     0,
+                     message,
+                     sizeof (message) / sizeof (TCHAR),
+                     NULL);
+       altos_set_last_error(error, message);
+}
+
+PUBLIC struct altos_list *
+altos_list_start(void)
+{
+       struct altos_list       *list = calloc(1, sizeof (struct altos_list));
+
+       if (!list)
+               return NULL;
+       list->dev_info = SetupDiGetClassDevs(NULL, "USB", NULL,
+                                            DIGCF_ALLCLASSES|DIGCF_PRESENT);
+       if (list->dev_info == INVALID_HANDLE_VALUE) {
+               altos_set_last_windows_error();
+               free(list);
+               return NULL;
+       }
+       list->index = 0;
+       return list;
+}
+
+PUBLIC int
+altos_list_next(struct altos_list *list, struct altos_device *device)
+{
+       SP_DEVINFO_DATA dev_info_data;
+       BYTE            port[128];
+       DWORD           port_len;
+       char            friendlyname[256];
+       BYTE            symbolic[256];
+       DWORD           symbolic_len;
+       HKEY            dev_key;
+       unsigned int    vid, pid;
+       int             serial;
+       HRESULT         result;
+       DWORD           friendlyname_type;
+       DWORD           friendlyname_len;
+
+       dev_info_data.cbSize = sizeof (SP_DEVINFO_DATA);
+       while(SetupDiEnumDeviceInfo(list->dev_info, list->index,
+                                   &dev_info_data))
+       {
+               list->index++;
+
+               dev_key = SetupDiOpenDevRegKey(list->dev_info, &dev_info_data,
+                                              DICS_FLAG_GLOBAL, 0, DIREG_DEV,
+                                              KEY_READ);
+               if (dev_key == INVALID_HANDLE_VALUE) {
+                       altos_set_last_windows_error();
+                       printf("cannot open device registry key\n");
+                       continue;
+               }
+
+               /* Fetch symbolic name for this device and parse out
+                * the vid/pid/serial info */
+               symbolic_len = sizeof(symbolic);
+               result = RegQueryValueEx(dev_key, "SymbolicName", NULL, NULL,
+                                        symbolic, &symbolic_len);
+               if (result != 0) {
+                       altos_set_last_windows_error();
+                       printf("cannot find SymbolicName value\n");
+                       RegCloseKey(dev_key);
+                       continue;
+               }
+               vid = pid = serial = 0;
+               sscanf((char *) symbolic + sizeof("\\??\\USB#VID_") - 1,
+                      "%04X", &vid);
+               sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_") - 1,
+                      "%04X", &pid);
+               sscanf((char *) symbolic + sizeof("\\??\\USB#VID_XXXX&PID_XXXX#") - 1,
+                      "%d", &serial);
+               if (!USB_IS_ALTUSMETRUM(vid, pid)) {
+                       RegCloseKey(dev_key);
+                       continue;
+               }
+
+               /* Fetch the com port name */
+               port_len = sizeof (port);
+               result = RegQueryValueEx(dev_key, "PortName", NULL, NULL,
+                                        port, &port_len);
+               RegCloseKey(dev_key);
+               if (result != 0) {
+                       altos_set_last_windows_error();
+                       printf("failed to get PortName\n");
+                       continue;
+               }
+
+               /* Fetch the device description which is the device name,
+                * with firmware that has unique USB ids */
+               friendlyname_len = sizeof (friendlyname);
+               if(!SetupDiGetDeviceRegistryProperty(list->dev_info,
+                                                    &dev_info_data,
+                                                    SPDRP_FRIENDLYNAME,
+                                                    &friendlyname_type,
+                                                    (BYTE *)friendlyname,
+                                                    sizeof(friendlyname),
+                                                    &friendlyname_len))
+               {
+                       altos_set_last_windows_error();
+                       printf("Failed to get friendlyname\n");
+                       continue;
+               }
+               device->vendor = vid;
+               device->product = pid;
+               device->serial = serial;
+               strcpy(device->name, friendlyname);
+
+               strcpy(device->path, (char *) port);
+               return 1;
+       }
+       result = GetLastError();
+       if (result != ERROR_NO_MORE_ITEMS) {
+               altos_set_last_windows_error();
+               printf ("SetupDiEnumDeviceInfo failed error %d\n", (int) result);
+       }
+       return 0;
+}
+
+PUBLIC void
+altos_list_finish(struct altos_list *list)
+{
+       SetupDiDestroyDeviceInfoList(list->dev_info);
+       free(list);
+}
+
+static int
+altos_queue_read(struct altos_file *file)
+{
+       DWORD   got;
+       if (file->pend_read)
+               return LIBALTOS_SUCCESS;
+
+       if (!ReadFile(file->handle, file->in_data, USB_BUF_SIZE, &got, &file->ov_read)) {
+               if (GetLastError() != ERROR_IO_PENDING) {
+                       altos_set_last_windows_error();
+                       return LIBALTOS_ERROR;
+               }
+               file->pend_read = TRUE;
+       } else {
+               file->pend_read = FALSE;
+               file->in_read = 0;
+               file->in_used = got;
+       }
+       return LIBALTOS_SUCCESS;
+}
+
+static int
+altos_wait_read(struct altos_file *file, int timeout)
+{
+       DWORD   ret;
+       DWORD   got;
+
+       if (!file->pend_read)
+               return LIBALTOS_SUCCESS;
+
+       if (!timeout)
+               timeout = INFINITE;
+
+       ret = WaitForSingleObject(file->ov_read.hEvent, timeout);
+       switch (ret) {
+       case WAIT_OBJECT_0:
+               if (!GetOverlappedResult(file->handle, &file->ov_read, &got, FALSE)) {
+                       altos_set_last_windows_error();
+                       return LIBALTOS_ERROR;
+               }
+               file->pend_read = FALSE;
+               file->in_read = 0;
+               file->in_used = got;
+               break;
+       case WAIT_TIMEOUT:
+               return LIBALTOS_TIMEOUT;
+               break;
+       default:
+               return LIBALTOS_ERROR;
+       }
+       return LIBALTOS_SUCCESS;
+}
+
+static int
+altos_fill(struct altos_file *file, int timeout)
+{
+       int     ret;
+
+       if (file->in_read < file->in_used)
+               return LIBALTOS_SUCCESS;
+
+       file->in_read = file->in_used = 0;
+
+       ret = altos_queue_read(file);
+       if (ret)
+               return ret;
+       ret = altos_wait_read(file, timeout);
+       if (ret)
+               return ret;
+
+       return LIBALTOS_SUCCESS;
+}
+
+PUBLIC int
+altos_flush(struct altos_file *file)
+{
+       DWORD           put;
+       unsigned char   *data = file->out_data;
+       int             used = file->out_used;
+       DWORD           ret;
+
+       while (used) {
+               if (!WriteFile(file->handle, data, used, &put, &file->ov_write)) {
+                       if (GetLastError() != ERROR_IO_PENDING) {
+                               altos_set_last_windows_error();
+                               return LIBALTOS_ERROR;
+                       }
+                       ret = WaitForSingleObject(file->ov_write.hEvent, INFINITE);
+                       switch (ret) {
+                       case WAIT_OBJECT_0:
+                               if (!GetOverlappedResult(file->handle, &file->ov_write, &put, FALSE)) {
+                                       altos_set_last_windows_error();
+                                       return LIBALTOS_ERROR;
+                               }
+                               break;
+                       default:
+                               altos_set_last_windows_error();
+                               return LIBALTOS_ERROR;
+                       }
+               }
+               data += put;
+               used -= put;
+       }
+       file->out_used = 0;
+       return LIBALTOS_SUCCESS;
+}
+
+PUBLIC struct altos_file *
+altos_open(struct altos_device *device)
+{
+       struct altos_file       *file = calloc (1, sizeof (struct altos_file));
+       char    full_name[64];
+       DCB dcbSerialParams = {0};
+       COMMTIMEOUTS timeouts;
+
+       if (!file)
+               return NULL;
+
+       strcpy(full_name, "\\\\.\\");
+       strcat(full_name, device->path);
+       file->handle = CreateFile(full_name, GENERIC_READ|GENERIC_WRITE,
+                                 0, NULL, OPEN_EXISTING,
+                                 FILE_FLAG_OVERLAPPED, NULL);
+       if (file->handle == INVALID_HANDLE_VALUE) {
+               altos_set_last_windows_error();
+               free(file);
+               return NULL;
+       }
+       file->ov_read.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+       file->ov_write.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+       timeouts.ReadIntervalTimeout = MAXDWORD;
+       timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
+       timeouts.ReadTotalTimeoutConstant = 1 << 30;    /* almost forever */
+       timeouts.WriteTotalTimeoutMultiplier = 0;
+       timeouts.WriteTotalTimeoutConstant = 0;
+       SetCommTimeouts(file->handle, &timeouts);
+
+       dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
+       if (!GetCommState(file->handle, &dcbSerialParams)) {
+               altos_set_last_windows_error();
+               CloseHandle(file->handle);
+               free(file);
+               return NULL;
+       }
+       dcbSerialParams.BaudRate = CBR_9600;
+       dcbSerialParams.ByteSize = 8;
+       dcbSerialParams.StopBits = ONESTOPBIT;
+       dcbSerialParams.Parity = NOPARITY;
+       if (!SetCommState(file->handle, &dcbSerialParams)) {
+               altos_set_last_windows_error();
+               CloseHandle(file->handle);
+               free(file);
+               return NULL;
+       }
+
+       return file;
+}
+
+PUBLIC void
+altos_close(struct altos_file *file)
+{
+       if (file->handle != INVALID_HANDLE_VALUE) {
+               CloseHandle(file->handle);
+               file->handle = INVALID_HANDLE_VALUE;
+       }
+}
+
+PUBLIC void
+altos_free(struct altos_file *file)
+{
+       altos_close(file);
+       free(file);
+}
+
+PUBLIC int
+altos_putchar(struct altos_file *file, char c)
+{
+       int     ret;
+
+       if (file->out_used == USB_BUF_SIZE) {
+               ret = altos_flush(file);
+               if (ret)
+                       return ret;
+       }
+       file->out_data[file->out_used++] = c;
+       if (file->out_used == USB_BUF_SIZE)
+               return altos_flush(file);
+       return LIBALTOS_SUCCESS;
+}
+
+PUBLIC int
+altos_getchar(struct altos_file *file, int timeout)
+{
+       int     ret;
+       while (file->in_read == file->in_used) {
+               if (file->handle == INVALID_HANDLE_VALUE)
+                       return LIBALTOS_ERROR;
+               ret = altos_fill(file, timeout);
+               if (ret)
+                       return ret;
+       }
+       return file->in_data[file->in_read++];
+}
+
+struct altos_bt_list *
+altos_bt_list_start(int inquiry_time)
+{
+       return NULL;
+}
+
+int
+altos_bt_list_next(struct altos_bt_list *bt_list,
+                  struct altos_bt_device *device)
+{
+       return 0;
+}
+
+void
+altos_bt_list_finish(struct altos_bt_list *bt_list)
+{
+       free(bt_list);
+}
+
+void
+altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device)
+{
+       strncpy(device->name, name, sizeof (device->name));
+       device->name[sizeof(device->name)-1] = '\0';
+       strncpy(device->addr, addr, sizeof (device->addr));
+       device->addr[sizeof(device->addr)-1] = '\0';
+}
+
+struct altos_file *
+altos_bt_open(struct altos_bt_device *device)
+{
+       return NULL;
+}
+
+#endif
diff --git a/altosui/libaltos/libaltos.dylib b/altosui/libaltos/libaltos.dylib
new file mode 100755 (executable)
index 0000000..1038817
Binary files /dev/null and b/altosui/libaltos/libaltos.dylib differ
diff --git a/altosui/libaltos/libaltos.h b/altosui/libaltos/libaltos.h
new file mode 100644 (file)
index 0000000..f90fbb8
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBALTOS_H_
+#define _LIBALTOS_H_
+
+#include <stdlib.h>
+
+#if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)
+# ifndef BUILD_STATIC
+#  ifdef BUILD_DLL
+#   define PUBLIC __declspec(dllexport)
+#  else
+#   define PUBLIC __declspec(dllimport)
+#  endif
+# endif /* BUILD_STATIC */
+#endif
+
+#ifndef PUBLIC
+# define PUBLIC
+#endif
+
+struct altos_device {
+       //%immutable;
+       int                             vendor;
+       int                             product;
+       int                             serial;
+       char                            name[256];
+       char                            path[256];
+       //%mutable;
+};
+
+struct altos_bt_device {
+       //%immutable;
+       char                            name[256];
+       char                            addr[20];
+       //%mutable;
+};
+
+struct altos_error {
+       int                             code;
+       char                            string[1024];
+};
+
+#define LIBALTOS_SUCCESS       0
+#define LIBALTOS_ERROR         -1
+#define LIBALTOS_TIMEOUT       -2
+
+/* Returns 0 for success, < 0 on error */
+PUBLIC int
+altos_init(void);
+
+PUBLIC void
+altos_fini(void);
+
+PUBLIC void
+altos_get_last_error(struct altos_error *error);
+
+PUBLIC struct altos_list *
+altos_list_start(void);
+
+/* Returns 1 for success, zero on end of list */
+PUBLIC int
+altos_list_next(struct altos_list *list, struct altos_device *device);
+
+PUBLIC void
+altos_list_finish(struct altos_list *list);
+
+PUBLIC struct altos_file *
+altos_open(struct altos_device *device);
+
+PUBLIC void
+altos_close(struct altos_file *file);
+
+PUBLIC void
+altos_free(struct altos_file *file);
+
+/* Returns < 0 for error */
+PUBLIC int
+altos_putchar(struct altos_file *file, char c);
+
+/* Returns < 0 for error */
+PUBLIC int
+altos_flush(struct altos_file *file);
+
+/* Returns < 0 for error or timeout. timeout of 0 == wait forever */
+PUBLIC int
+altos_getchar(struct altos_file *file, int timeout);
+
+PUBLIC struct altos_bt_list *
+altos_bt_list_start(int inquiry_time);
+
+PUBLIC int
+altos_bt_list_next(struct altos_bt_list *list, struct altos_bt_device *device);
+
+PUBLIC void
+altos_bt_list_finish(struct altos_bt_list *list);
+
+PUBLIC void
+altos_bt_fill_in(char *name, char *addr, struct altos_bt_device *device);
+
+PUBLIC struct altos_file *
+altos_bt_open(struct altos_bt_device *device);
+
+#endif /* _LIBALTOS_H_ */
diff --git a/altosui/libaltos/libaltos.i0 b/altosui/libaltos/libaltos.i0
new file mode 100644 (file)
index 0000000..d06468f
--- /dev/null
@@ -0,0 +1,5 @@
+%module libaltos
+%{
+#include "libaltos.h"
+%}
+
diff --git a/ao-bringup/.gitignore b/ao-bringup/.gitignore
new file mode 100644 (file)
index 0000000..dc7beb5
--- /dev/null
@@ -0,0 +1,2 @@
+ao_led_blink
+ao_radio_xmit
diff --git a/ao-bringup/Makefile b/ao-bringup/Makefile
new file mode 100644 (file)
index 0000000..7fdde98
--- /dev/null
@@ -0,0 +1,58 @@
+CC=sdcc
+DEBUG=--debug
+
+CFLAGS=--model-small --debug -I../src/core -I../src/cc1111
+
+LDFLAGS=--out-fmt-ihx --code-loc 0xf000 --xram-loc 0xf400 --xram-size 1024 --iram-size 0xff
+
+INC = \
+       ao_bringup.h
+
+BRINGUP_SRC = ao_init.c
+
+BRINGUP_REL=$(BRINGUP_SRC:.c=.rel)
+
+XMIT_SRC = \
+       ao_radio_init.c \
+       ao_radio_xmit.c
+XMIT_REL=$(XMIT_SRC:.c=.rel) $(BRINGUP_REL)
+
+LED_SRC = \
+       ao_led_blink.c
+
+LED_REL=$(LED_SRC:.c=.rel) $(BRINGUP_REL)
+
+SRC=$(BRINGUP_SRC) $(XMIT_SRC) $(LED_SRC)
+
+ADB=$(SRC:.c=.adb)
+ASM=$(SRC:.c=.asm)
+LNK=$(SRC:.c=.lnk)
+LST=$(SRC:.c=.lst)
+REL=$(SRC:.c=.rel)
+RST=$(SRC:.c=.rst)
+SYM=$(SRC:.c=.sym)
+
+PROGS=ao_radio_xmit.ihx ao_led_blink.ihx
+
+PCDB=$(PROGS:.ihx=.cdb)
+PLNK=$(PROGS:.ihx=.lnk)
+PMAP=$(PROGS:.ihx=.map)
+PMEM=$(PROGS:.ihx=.mem)
+PAOM=$(PROGS:.ihx=)
+
+%.rel : %.c
+       $(CC) -c $(CFLAGS) -o$*.rel $<
+
+all: $(PROGS)
+
+ao_radio_xmit.ihx: $(XMIT_REL)
+       $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(XMIT_REL)
+
+ao_led_blink.ihx: $(LED_REL)
+       $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(LED_REL)
+
+clean:
+       rm -f $(ADB) $(ASM) $(LNK) $(LST) $(REL) $(RST) $(SYM)
+       rm -f $(PROGS) $(PCDB) $(PLNK) $(PMAP) $(PMEM) $(PAOM)
+
+install:
diff --git a/ao-bringup/ao_bringup.h b/ao-bringup/ao_bringup.h
new file mode 100644 (file)
index 0000000..99fa4fb
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BRINGUP_H_
+#define _AO_BRINGUP_H_
+
+#include <stdint.h>
+#include <cc1111.h>
+
+void
+ao_radio_init(void);
+
+void
+ao_radio_idle(void);
+
+void
+ao_init(void);
+
+#endif
diff --git a/ao-bringup/ao_init.c b/ao-bringup/ao_init.c
new file mode 100644 (file)
index 0000000..5dababd
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_bringup.h"
+
+void
+ao_init(void)
+{
+       CLKCON = 0;
+       while (!(SLEEP & SLEEP_XOSC_STB))
+               ;
+       P1 = 0;
+       P1DIR = 3;
+}
diff --git a/ao-bringup/ao_led_blink.c b/ao-bringup/ao_led_blink.c
new file mode 100644 (file)
index 0000000..1e4c143
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "ao_bringup.h"
+
+#define nop()  _asm nop _endasm;
+
+void
+delay (unsigned char n)
+{
+       unsigned char i = 0, j = 0;
+
+       n <<= 1;
+       while (--n != 0)
+               while (--j != 0)
+                       while (--i != 0)
+                               nop();
+}
+
+main()
+{
+       ao_init();
+       /* Set p1_0 and p1_1 to output */
+       P1DIR = 0x03;
+       P1INP = 0x00;
+       for (;;) {
+               P1 = 1;
+               delay(5);
+               P1 = 2;
+               delay(5);
+               P1 = 3;
+               delay(5);
+               P1 = 0;
+               delay(5);
+       }
+}
diff --git a/ao-bringup/ao_radio_init.c b/ao-bringup/ao_radio_init.c
new file mode 100644 (file)
index 0000000..1193250
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_bringup.h"
+
+/* Values from SmartRF® Studio for:
+ *
+ * Deviation:  20.507812 kHz
+ * Datarate:   38.360596 kBaud
+ * Modulation: GFSK
+ * RF Freq:    434.549927 MHz
+ * Channel:    99.975586 kHz
+ * Channel:    0
+ * RX filter:  93.75 kHz
+ */
+
+/*
+ * For 434.550MHz, the frequency value is:
+ *
+ * 434.550e6 / (24e6 / 2**16) = 1186611.2
+ */
+
+#define FREQ_CONTROL   1186611
+
+/*
+ * For IF freq of 140.62kHz, the IF value is:
+ *
+ * 140.62e3 / (24e6 / 2**10) = 6
+ */
+
+#define IF_FREQ_CONTROL        6
+
+/*
+ * For channel bandwidth of 93.75 kHz, the CHANBW_E and CHANBW_M values are
+ *
+ * BW = 24e6 / (8 * (4 + M) * 2 ** E)
+ *
+ * So, M = 0 and E = 3
+ */
+
+#define CHANBW_M       0
+#define CHANBW_E       3
+
+/*
+ * For a symbol rate of 38360kBaud, the DRATE_E and DRATE_M values are:
+ *
+ * R = (256 + M) * 2** E * 24e6 / 2**28
+ *
+ * So M is 163 and E is 10
+ */
+
+#define DRATE_E                10
+#define DRATE_M                163
+
+/*
+ * For a channel deviation of 20.5kHz, the DEVIATION_E and DEVIATION_M values are:
+ *
+ * F = 24e6/2**17 * (8 + DEVIATION_M) * 2**DEVIATION_E
+ *
+ * So M is 6 and E is 3
+ */
+
+#define DEVIATION_M    6
+#define DEVIATION_E    3
+
+/*
+ * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone),
+ * so the DRATE_E and DRATE_M values are:
+ *
+ * M is 94 and E is 6
+ *
+ * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
+ */
+
+#define RDF_DRATE_E    6
+#define RDF_DRATE_M    94
+#define RDF_PACKET_LEN 50
+
+/*
+ * RDF deviation should match the normal NFM value of 5kHz
+ *
+ * M is 6 and E is 1
+ *
+ */
+
+#define RDF_DEVIATION_M        6
+#define RDF_DEVIATION_E        1
+
+/* This are from the table for 433MHz */
+
+#define RF_POWER_M30_DBM       0x12
+#define RF_POWER_M20_DBM       0x0e
+#define RF_POWER_M15_DBM       0x1d
+#define RF_POWER_M10_DBM       0x34
+#define RF_POWER_M5_DBM                0x2c
+#define RF_POWER_0_DBM         0x60
+#define RF_POWER_5_DBM         0x84
+#define RF_POWER_7_DBM         0xc8
+#define RF_POWER_10_DBM                0xc0
+
+#define RF_POWER               RF_POWER_10_DBM
+
+static __code uint8_t radio_setup[] = {
+       RF_PA_TABLE7_OFF,       RF_POWER,
+       RF_PA_TABLE6_OFF,       RF_POWER,
+       RF_PA_TABLE5_OFF,       RF_POWER,
+       RF_PA_TABLE4_OFF,       RF_POWER,
+       RF_PA_TABLE3_OFF,       RF_POWER,
+       RF_PA_TABLE2_OFF,       RF_POWER,
+       RF_PA_TABLE1_OFF,       RF_POWER,
+       RF_PA_TABLE0_OFF,       RF_POWER,
+
+       RF_FREQ2_OFF,           (FREQ_CONTROL >> 16) & 0xff,
+       RF_FREQ1_OFF,           (FREQ_CONTROL >> 8) & 0xff,
+       RF_FREQ0_OFF,           (FREQ_CONTROL) & 0xff,
+
+       RF_FSCTRL1_OFF,         (IF_FREQ_CONTROL << RF_FSCTRL1_FREQ_IF_SHIFT),
+       RF_FSCTRL0_OFF,         (0 << RF_FSCTRL0_FREQOFF_SHIFT),
+
+       RF_MDMCFG4_OFF,         ((CHANBW_E << RF_MDMCFG4_CHANBW_E_SHIFT) |
+                                (CHANBW_M << RF_MDMCFG4_CHANBW_M_SHIFT) |
+                                (DRATE_E << RF_MDMCFG4_DRATE_E_SHIFT)),
+       RF_MDMCFG3_OFF,         (DRATE_M << RF_MDMCFG3_DRATE_M_SHIFT),
+       RF_MDMCFG2_OFF,         (RF_MDMCFG2_DEM_DCFILT_OFF |
+                                RF_MDMCFG2_MOD_FORMAT_GFSK |
+                                RF_MDMCFG2_SYNC_MODE_15_16_THRES),
+       RF_MDMCFG1_OFF,         (RF_MDMCFG1_FEC_EN |
+                                RF_MDMCFG1_NUM_PREAMBLE_4 |
+                                (2 << RF_MDMCFG1_CHANSPC_E_SHIFT)),
+       RF_MDMCFG0_OFF,         (17 << RF_MDMCFG0_CHANSPC_M_SHIFT),
+
+       RF_CHANNR_OFF,          0,
+
+       RF_DEVIATN_OFF,         ((DEVIATION_E << RF_DEVIATN_DEVIATION_E_SHIFT) |
+                                (DEVIATION_M << RF_DEVIATN_DEVIATION_M_SHIFT)),
+
+       /* SmartRF says set LODIV_BUF_CURRENT_TX to 0
+        * And, we're not using power ramping, so use PA_POWER 0
+        */
+       RF_FREND0_OFF,          ((1 << RF_FREND0_LODIV_BUF_CURRENT_TX_SHIFT) |
+                                (0 << RF_FREND0_PA_POWER_SHIFT)),
+
+       RF_FREND1_OFF,          ((1 << RF_FREND1_LNA_CURRENT_SHIFT) |
+                                (1 << RF_FREND1_LNA2MIX_CURRENT_SHIFT) |
+                                (1 << RF_FREND1_LODIV_BUF_CURRENT_RX_SHIFT) |
+                                (2 << RF_FREND1_MIX_CURRENT_SHIFT)),
+
+       RF_FSCAL3_OFF,          0xE9,
+       RF_FSCAL2_OFF,          0x0A,
+       RF_FSCAL1_OFF,          0x00,
+       RF_FSCAL0_OFF,          0x1F,
+
+       RF_TEST2_OFF,           0x88,
+       RF_TEST1_OFF,           0x31,
+       RF_TEST0_OFF,           0x09,
+
+       /* default sync values */
+       RF_SYNC1_OFF,           0xD3,
+       RF_SYNC0_OFF,           0x91,
+
+       /* max packet length */
+       RF_PKTLEN_OFF,          64,
+
+       RF_PKTCTRL1_OFF,        ((1 << PKTCTRL1_PQT_SHIFT)|
+                                PKTCTRL1_APPEND_STATUS|
+                                PKTCTRL1_ADR_CHK_NONE),
+       RF_PKTCTRL0_OFF,        (RF_PKTCTRL0_WHITE_DATA|
+                                RF_PKTCTRL0_PKT_FORMAT_NORMAL|
+                                RF_PKTCTRL0_CRC_EN|
+                                RF_PKTCTRL0_LENGTH_CONFIG_FIXED),
+       RF_ADDR_OFF,            0x00,
+       RF_MCSM2_OFF,           (RF_MCSM2_RX_TIME_END_OF_PACKET),
+       RF_MCSM1_OFF,           (RF_MCSM1_CCA_MODE_RSSI_BELOW_UNLESS_RECEIVING|
+                                RF_MCSM1_RXOFF_MODE_IDLE|
+                                RF_MCSM1_TXOFF_MODE_IDLE),
+       RF_MCSM0_OFF,           (RF_MCSM0_FS_AUTOCAL_FROM_IDLE|
+                                RF_MCSM0_MAGIC_3|
+                                RF_MCSM0_CLOSE_IN_RX_0DB),
+       RF_FOCCFG_OFF,          (RF_FOCCFG_FOC_PRE_K_3K,
+                                RF_FOCCFG_FOC_POST_K_PRE_K,
+                                RF_FOCCFG_FOC_LIMIT_BW_OVER_4),
+       RF_BSCFG_OFF,           (RF_BSCFG_BS_PRE_K_2K|
+                                RF_BSCFG_BS_PRE_KP_3KP|
+                                RF_BSCFG_BS_POST_KI_PRE_KI|
+                                RF_BSCFG_BS_POST_KP_PRE_KP|
+                                RF_BSCFG_BS_LIMIT_0),
+       RF_AGCCTRL2_OFF,        0x43,
+       RF_AGCCTRL1_OFF,        0x40,
+       RF_AGCCTRL0_OFF,        0x91,
+
+       RF_IOCFG2_OFF,          0x00,
+       RF_IOCFG1_OFF,          0x00,
+       RF_IOCFG0_OFF,          0x00,
+
+       RF_MDMCFG4_OFF,         ((CHANBW_E << RF_MDMCFG4_CHANBW_E_SHIFT) |
+                                (CHANBW_M << RF_MDMCFG4_CHANBW_M_SHIFT) |
+                                (DRATE_E << RF_MDMCFG4_DRATE_E_SHIFT)),
+       RF_MDMCFG3_OFF,         (DRATE_M << RF_MDMCFG3_DRATE_M_SHIFT),
+       RF_MDMCFG2_OFF,         (RF_MDMCFG2_DEM_DCFILT_OFF |
+                                RF_MDMCFG2_MOD_FORMAT_GFSK |
+                                RF_MDMCFG2_SYNC_MODE_15_16_THRES),
+       RF_MDMCFG1_OFF,         (RF_MDMCFG1_FEC_EN |
+                                RF_MDMCFG1_NUM_PREAMBLE_4 |
+                                (2 << RF_MDMCFG1_CHANSPC_E_SHIFT)),
+
+       RF_DEVIATN_OFF,         ((DEVIATION_E << RF_DEVIATN_DEVIATION_E_SHIFT) |
+                                (DEVIATION_M << RF_DEVIATN_DEVIATION_M_SHIFT)),
+
+       /* max packet length */
+       RF_PKTLEN_OFF,          0xff,
+       RF_PKTCTRL1_OFF,        ((1 << PKTCTRL1_PQT_SHIFT)|
+                                PKTCTRL1_APPEND_STATUS|
+                                PKTCTRL1_ADR_CHK_NONE),
+       RF_PKTCTRL0_OFF,        (RF_PKTCTRL0_WHITE_DATA|
+                                RF_PKTCTRL0_PKT_FORMAT_NORMAL|
+                                RF_PKTCTRL0_CRC_EN|
+                                RF_PKTCTRL0_LENGTH_CONFIG_FIXED),
+};
+
+void
+ao_radio_idle(void)
+{
+       if (RF_MARCSTATE != RF_MARCSTATE_IDLE)
+       {
+               RFST = RFST_SIDLE;
+               do {
+                       ;
+               } while (RF_MARCSTATE != RF_MARCSTATE_IDLE);
+       }
+}
+
+void
+ao_radio_init(void) {
+       uint8_t i;
+       for (i = 0; i < sizeof (radio_setup); i += 2)
+               RF[radio_setup[i]] = radio_setup[i+1];
+}
diff --git a/ao-bringup/ao_radio_test.c b/ao-bringup/ao_radio_test.c
new file mode 100644 (file)
index 0000000..8efb03c
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_bringup.h"
diff --git a/ao-bringup/ao_radio_xmit.c b/ao-bringup/ao_radio_xmit.c
new file mode 100644 (file)
index 0000000..be3333a
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao_bringup.h"
+
+main ()
+{
+       ao_init();
+       ao_radio_init();
+       ao_radio_idle();
+       RFST = RFST_STX;
+       P1 = 1;
+       for (;;);
+}
diff --git a/ao-bringup/megametrum.cfg b/ao-bringup/megametrum.cfg
new file mode 100644 (file)
index 0000000..e95c6f2
--- /dev/null
@@ -0,0 +1,4 @@
+# openocd config for MegaMetrum using the Olimex ARM-USB-OCD dongle
+
+source /opt/stm32/share/openocd/scripts/interface/olimex-arm-usb-ocd.cfg
+source /opt/stm32/share/openocd/scripts/target/stm32l.cfg
diff --git a/ao-bringup/megametrum.gdb b/ao-bringup/megametrum.gdb
new file mode 100644 (file)
index 0000000..964ae1f
--- /dev/null
@@ -0,0 +1,2 @@
+target remote localhost:3333
+monitor poll
diff --git a/ao-bringup/testplan b/ao-bringup/testplan
new file mode 100644 (file)
index 0000000..2c0e4e0
--- /dev/null
@@ -0,0 +1,17 @@
+Low level hardware tests
+
+ * cpu
+ * barometer
+ * accelerometer
+ * flash
+ * gps
+ * igniter continuity
+ * igniters
+ * radio calibration
+ * led
+
+Higher level tests
+
+ * USB serial communication
+ * memory flashing
+ * reading/writing eeprom
diff --git a/ao-bringup/turnon_telebt b/ao-bringup/turnon_telebt
new file mode 100755 (executable)
index 0000000..3b8bd3e
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+if [ -x ../ao-tools/ao-load/ao-load ]; then
+       AOLOAD=../ao-tools/ao-load/ao-load
+elif [ -x /usr/bin/ao-load ]; then
+       AOLOAD=/usr/bin/ao-load
+else
+       echo "Can't find ao-load!  Aborting."
+       exit 1
+fi
+
+if [ -x ../ao-tools/ao-rawload/ao-rawload ]; then
+       RAWLOAD=../ao-tools/ao-rawload/ao-rawload
+elif [ -x /usr/bin/ao-rawload ]; then
+       RAWLOAD=/usr/bin/ao-rawload
+else
+       echo "Can't find ao-rawload!  Aborting."
+       exit 1
+fi
+
+echo "TeleBT v0.1 Turn-On and Calibration Program"
+echo "Copyright 2011 by Bdale Garbee.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\tTeleBT v0.1 powered from USB"
+echo "\t\twith TeleDonle (on /dev/ttyACM0) cabled to debug header"
+echo "\t\twith coax from SMA to frequency counter"
+echo
+echo -n "TeleBT serial number: "
+read SERIAL
+
+echo $RAWLOAD
+
+$RAWLOAD -D 100 -r ao_led_blink.ihx
+echo "LEDs should be blinking"
+sleep 5
+
+$RAWLOAD -D 100 -r ao_radio_xmit.ihx
+echo -n "Generating RF carrier.  Please enter measured frequency: "
+read FREQ
+
+CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"`
+
+echo "Programming flash with cal value " $CAL_VALUE
+$AOLOAD -D 100 --cal $CAL_VALUE /usr/share/altos/telebt-v0.1*.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_teledongle b/ao-bringup/turnon_teledongle
new file mode 100755 (executable)
index 0000000..320cd8f
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+if [ -x ../ao-tools/ao-load/ao-load ]; then
+       AOLOAD=../ao-tools/ao-load/ao-load
+elif [ -x /usr/bin/ao-load ]; then
+       AOLOAD=/usr/bin/ao-load
+else
+       echo "Can't find ao-load!  Aborting."
+       exit 1
+fi
+
+if [ -x ../ao-tools/ao-rawload/ao-rawload ]; then
+       RAWLOAD=../ao-tools/ao-rawload/ao-rawload
+elif [ -x /usr/bin/ao-rawload ]; then
+       RAWLOAD=/usr/bin/ao-rawload
+else
+       echo "Can't find ao-rawload!  Aborting."
+       exit 1
+fi
+
+echo "TeleDongle v0.2 Turn-On and Calibration Program"
+echo "Copyright 2010 by Bdale Garbee.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\tTeleDongle v0.2 powered from USB"
+echo "\t\twith TIdongle (on /dev/ttyACM0) cabled to debug header"
+echo "\t\twith coax from SMA to frequency counter"
+echo
+echo -n "TeleDongle serial number: "
+read SERIAL
+
+echo $RAWLOAD
+
+$RAWLOAD -D 100 -r ao_led_blink.ihx
+echo "LEDs should be blinking"
+sleep 5
+
+$RAWLOAD -D 100 -r ao_radio_xmit.ihx
+echo -n "Generating RF carrier.  Please enter measured frequency: "
+read FREQ
+
+CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"`
+
+echo "Programming flash with cal value " $CAL_VALUE
+$AOLOAD -D 100 --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
new file mode 100755 (executable)
index 0000000..faf49d4
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+if [ -x ../ao-tools/ao-load/ao-load ]; then
+       AOLOAD=../ao-tools/ao-load/ao-load
+elif [ -x /usr/bin/ao-load ]; then
+       AOLOAD=/usr/bin/ao-load
+else
+       echo "Can't find ao-load!  Aborting."
+       exit 1
+fi
+
+if [ -x ../ao-tools/ao-rawload/ao-rawload ]; then
+       RAWLOAD=../ao-tools/ao-rawload/ao-rawload
+elif [ -x /usr/bin/ao-rawload ]; then
+       RAWLOAD=/usr/bin/ao-rawload
+else
+       echo "Can't find ao-rawload!  Aborting."
+       exit 1
+fi
+
+echo "TeleMetrum v1.2 Turn-On and Calibration Program"
+echo "Copyright 2010 by Bdale Garbee.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\tTeleMetrum v1.2 powered from USB"
+echo "\t\twith TeleDongle (on /dev/ttyACM0) cabled to debug header"
+echo "\t\twith coax from UHF to frequency counter"
+echo
+echo -n "TeleMetrum serial number: "
+read SERIAL
+
+echo $RAWLOAD
+
+$RAWLOAD --device 100 -r ao_led_blink.ihx
+echo "the red LED should be blinking"
+sleep 5
+
+$RAWLOAD --device 100 -r ao_radio_xmit.ihx
+echo -n "Generating RF carrier.  Please enter measured frequency: "
+read FREQ
+
+CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"`
+
+echo "Programming flash with cal value " $CAL_VALUE
+$AOLOAD --device 100 --cal $CAL_VALUE \
+       /usr/share/altos/stable/telemetrum-v1.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_telemini b/ao-bringup/turnon_telemini
new file mode 100755 (executable)
index 0000000..4450d6f
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+if [ -x ../ao-tools/ao-load/ao-load ]; then
+       AOLOAD=../ao-tools/ao-load/ao-load
+elif [ -x /usr/bin/ao-load ]; then
+       AOLOAD=/usr/bin/ao-load
+else
+       echo "Can't find ao-load!  Aborting."
+       exit 1
+fi
+
+if [ -x ../ao-tools/ao-rawload/ao-rawload ]; then
+       RAWLOAD=../ao-tools/ao-rawload/ao-rawload
+elif [ -x /usr/bin/ao-rawload ]; then
+       RAWLOAD=/usr/bin/ao-rawload
+else
+       echo "Can't find ao-rawload!  Aborting."
+       exit 1
+fi
+
+echo "TeleMini v1.0 Turn-On and Calibration Program"
+echo "Copyright 2011 by Bdale Garbee.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\tTeleMini v1.0 powered from LiPo"
+echo "\t\twith TeleDongle (on /dev/ttyACM0) cabled to debug header"
+echo "\t\twith frequency counter able to sample RF output"
+echo
+echo -n "TeleMini serial number: "
+read SERIAL
+
+echo $RAWLOAD
+
+$RAWLOAD -D 100 -r ao_led_blink.ihx
+echo "LEDs should be blinking"
+sleep 5
+
+$RAWLOAD -D 100 -r ao_radio_xmit.ihx
+echo -n "Generating RF carrier.  Please enter measured frequency: "
+read FREQ
+
+CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"`
+
+echo "Programming flash with cal value " $CAL_VALUE
+$AOLOAD -D 100 --cal $CAL_VALUE /usr/share/altos/stable/telemini-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-bringup/turnon_teleshield b/ao-bringup/turnon_teleshield
new file mode 100755 (executable)
index 0000000..e9f651f
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+if [ -x ../ao-tools/ao-load/ao-load ]; then
+       AOLOAD=../ao-tools/ao-load/ao-load
+elif [ -x /usr/bin/ao-load ]; then
+       AOLOAD=/usr/bin/ao-load
+else
+       echo "Can't find ao-load!  Aborting."
+       exit 1
+fi
+
+if [ -x ../ao-tools/ao-rawload/ao-rawload ]; then
+       RAWLOAD=../ao-tools/ao-rawload/ao-rawload
+elif [ -x /usr/bin/ao-rawload ]; then
+       RAWLOAD=/usr/bin/ao-rawload
+else
+       echo "Can't find ao-rawload!  Aborting."
+       exit 1
+fi
+
+echo "TeleShield v0.1 Turn-On and Calibration Program"
+echo "Copyright 2012 by Bdale Garbee.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\tTeleShield v0.1 powered from USB"
+echo "\t\twith TeleDongle (on /dev/ttyACM0) cabled to debug header"
+echo "\t\twith coax from SMA to frequency counter"
+echo
+echo -n "TeleShield serial number: "
+read SERIAL
+
+echo $RAWLOAD
+
+$RAWLOAD -D 100 -r ao_led_blink.ihx
+echo "LEDs should be blinking"
+sleep 5
+
+$RAWLOAD -D 100 -r ao_radio_xmit.ihx
+echo -n "Generating RF carrier.  Please enter measured frequency: "
+read FREQ
+
+CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"`
+
+echo "Programming flash with cal value " $CAL_VALUE
+$AOLOAD -D 100 --cal $CAL_VALUE /home/bdale/debian/altos/src/teleshield-v0.1/*.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_teleterra b/ao-bringup/turnon_teleterra
new file mode 100755 (executable)
index 0000000..b63da2f
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+if [ -x ../ao-tools/ao-load/ao-load ]; then
+       AOLOAD=../ao-tools/ao-load/ao-load
+elif [ -x /usr/bin/ao-load ]; then
+       AOLOAD=/usr/bin/ao-load
+else
+       echo "Can't find ao-load!  Aborting."
+       exit 1
+fi
+
+if [ -x ../ao-tools/ao-rawload/ao-rawload ]; then
+       RAWLOAD=../ao-tools/ao-rawload/ao-rawload
+elif [ -x /usr/bin/ao-rawload ]; then
+       RAWLOAD=/usr/bin/ao-rawload
+else
+       echo "Can't find ao-rawload!  Aborting."
+       exit 1
+fi
+
+echo "TeleTerra v0.2 Turn-On and Calibration Program"
+echo "Copyright 2012 by Bdale Garbee.  Released under GPL v2"
+echo
+echo "Expectations:"
+echo "\tTeleTerra v0.2 powered from USB"
+echo "\t\twith TeleDongle (on /dev/ttyACM0) cabled to debug header"
+echo "\t\twith coax from SMA to frequency counter"
+echo
+echo -n "TeleTerra serial number: "
+read SERIAL
+
+echo $RAWLOAD
+
+$RAWLOAD -D 100 -r ao_led_blink.ihx
+echo "LEDs should be blinking"
+sleep 5
+
+$RAWLOAD -D 100 -r ao_radio_xmit.ihx
+echo -n "Generating RF carrier.  Please enter measured frequency: "
+read FREQ
+
+CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"`
+
+echo "Programming flash with cal value " $CAL_VALUE
+$AOLOAD -D 100 --cal $CAL_VALUE /home/bdale/debian/altos/src/teleterra-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"
index 98b88f38b4382dfc8d5374874d5e84f966c99757..257fdaece07f7d4a060c1125cdd305700a8faaba 100644 (file)
@@ -1 +1 @@
-SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-load
+SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list ao-load ao-telem ao-stmload ao-send-telem
index f1e2c11199122a5ca4b1ca5b19a5c59fb7f839b7..21b83a3dbc2b6c01327f05fe1e72ae59111b400a 100644 (file)
@@ -34,6 +34,7 @@ struct ccdbg *s51_dbg;
 int s51_interrupted = 0;
 int s51_monitor = 0;
 char *s51_tty = NULL;
+char *s51_device = NULL;
 
 static FILE *s51_input;
 static FILE *s51_output;
@@ -52,6 +53,7 @@ void s51_sigint()
 
 static const struct option options[] = {
        { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
        { 0, 0, 0, 0 },
 };
 
@@ -114,6 +116,9 @@ main(int argc, char **argv)
                case 'T':
                        s51_tty = optarg;
                        break;
+               case 'D':
+                       s51_device = optarg;
+                       break;
                }
        }
        if (s51_port) {
index 825d0e9c74972d72bf809ed69c0d42e0624af89f..dcb9099df8cbe8d1742a2f877b9584bc5fa9c3a8 100644 (file)
@@ -195,6 +195,11 @@ command_read (void)
        enum command_result result;
        struct command_function *func;
 
+       if (!s51_tty) {
+               if (!s51_device)
+                       s51_device = getenv("AO_DBG_DEVICE");
+               s51_tty = cc_usbdevs_find_by_arg(s51_device, "TIDongle");
+       }
        s51_dbg = ccdbg_open (s51_tty);
        if (!s51_dbg)
                exit(1);
index a850c45407ed76b7e845c92ac2ea109bc1cee84f..00d3ac86318e24c83c21fc76e0a5c691cfab9dc6 100644 (file)
@@ -35,6 +35,9 @@ ao-dbg \- hex debugger for cc1111 processors
 [\-h]
 [\-m]
 [\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
 .SH DESCRIPTION
 .I ao-dbg
 connects to a cc1111 processor through either a suitable cc1111 board
@@ -80,11 +83,26 @@ This should print a usage message, but does nothing useful currently.
 .IP "\-m"
 This option is not present in the original 8051 emulator, and causes ao-dbg to
 dump all commands and replies that are received from and sent to sdcdb.
-.IP "\-T"
+.TP
+\-T tty-device | --tty tty-device
 This selects which tty device the debugger uses to communicate with
 the target device. The special name 'BITBANG' directs ao-dbg to use
 the cp2103 connection, otherwise this should be a usb serial port
 connected to a suitable cc1111 debug node.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMetrum:2
+.br
+TeleMetrum
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
 .SH COMMANDS
 Once started, ao-dbg connects to the cc1111 and then reads and
 executes commands, either from stdin, or the nework connection to
index c1789d10e30cfb930f380c7089763b05b51fecce..edc650a506c8d99c2c2a20de7ba43443a0726b3e 100644 (file)
  */
 
 #include <ccdbg.h>
+#include <cc.h>
 
 extern char *s51_prompt;
 extern struct ccdbg *s51_dbg;
 extern int s51_interrupted;
 extern int s51_monitor;
 extern char *s51_tty;
+extern char *s51_device;
 
 enum command_result {
        command_success, command_debug, command_syntax, command_interrupt, command_error,
diff --git a/ao-tools/ao-dumplog/Makefile.am b/ao-tools/ao-dumplog/Makefile.am
new file mode 100644 (file)
index 0000000..a80cac3
--- /dev/null
@@ -0,0 +1,12 @@
+bin_PROGRAMS=ao-dumplog
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS)
+AO_DUMPLOG_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_dumplog_DEPENDENCIES = $(AO_DUMPLOG_LIBS)
+
+ao_dumplog_LDADD=$(AO_DUMPLOG_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS)
+
+ao_dumplog_SOURCES = ao-dumplog.c
+
+man_MANS = ao-dumplog.1
diff --git a/ao-tools/ao-dumplog/ao-dumplog.1 b/ao-tools/ao-dumplog/ao-dumplog.1
new file mode 100644 (file)
index 0000000..d381e33
--- /dev/null
@@ -0,0 +1,74 @@
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-DUMPLOG 1 "ao-dumplog" ""
+.SH NAME
+ao-dumplog \- Store flight log from TeleMetrum device
+.SH SYNOPSIS
+.B "ao-dumplog"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+[\--R\fP]
+[\--remote\fP]
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device ao-dumplog uses to communicate with
+the target device.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMetrum:2
+.br
+TeleMetrum
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
+.TP
+\-R | --remote
+This uses the command radio link to download the log from TeleMetrum
+through a TeleDongle.
+.SH DESCRIPTION
+.I ao-dumplog
+downloads the flight log from a connected TeleMetrum device and stores
+it to the configured flight log directory using a name of the form
+.IP
+\fIyyyy\fP-\fImm\fP-\fIdd\fP-serialP-\fIsss\fP-flight-\fIfff\fP.eeprom
+.PP
+\fIyyyy\fP is the current year
+.br
+\fImm\fP is the current month
+.br
+\fIdd\fP is the current day
+.br
+\fIsss\fP is the device serial number
+.br
+\fIfff\fP is a flight sequence number (to make filenames unique)
+.SH USAGE
+.I ao-dumplog
+connects to the specified target device and dumps the stored flight
+log.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-dumplog/ao-dumplog.c b/ao-tools/ao-dumplog/ao-dumplog.c
new file mode 100644 (file)
index 0000000..6d4fa5b
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "cc-usb.h"
+#include "cc.h"
+
+#define NUM_BLOCK      512
+
+static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "remote", .has_arg = 0, .val = 'R' },
+       { .name = "channel", .has_arg = 1, .val = 'C' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [--remote] [--channel <radio-channel>]\n", program);
+       exit(1);
+}
+
+static uint8_t
+log_checksum(int d[8])
+{
+       uint8_t sum = 0x5a;
+       int     i;
+
+       for (i = 0; i < 8; i++)
+               sum += (uint8_t) d[i];
+       return -sum;
+}
+
+static const char *state_names[] = {
+       "startup",
+       "idle",
+       "pad",
+       "boost",
+       "fast",
+       "coast",
+       "drogue",
+       "main",
+       "landed",
+       "invalid"
+};
+
+
+int
+main (int argc, char **argv)
+{
+       struct cc_usb   *cc;
+       char            *tty = NULL;
+       char            *device = NULL;
+       int             c;
+       char            line[8192];
+       FILE            *out;
+       char            *filename;
+       int             serial_number = 0;
+       int             channel = 0;
+       int             flight = 0;
+       char            cmd;
+       int             tick, a, b;
+       int             block;
+       int             addr;
+       int             received_addr;
+       int             data[8];
+       int             done;
+       int             column;
+       int             remote = 0;
+       int             any_valid;
+       int             invalid;
+       char            serial_line[8192];
+
+       while ((c = getopt_long(argc, argv, "T:D:C:R", options, NULL)) != -1) {
+               switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'R':
+                       remote = 1;
+                       break;
+               case 'C':
+                       channel = atoi(optarg);
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+       if (!tty) {
+               if (remote)
+                       tty = cc_usbdevs_find_by_arg(device, "TeleDongle");
+               else
+                       tty = cc_usbdevs_find_by_arg(device, "TeleMetrum");
+       }
+       if (!tty)
+               tty = getenv("ALTOS_TTY");
+       if (!tty)
+               tty="/dev/ttyACM0";
+       cc = cc_usb_open(tty);
+       if (!cc)
+               exit(1);
+       if (remote)
+               cc_usb_open_remote(cc, channel);
+       /* send a 'version' command followed by a 'log' command */
+       cc_usb_printf(cc, "v\n");
+       out = NULL;
+       for (;;) {
+               cc_usb_getline(cc, line, sizeof (line));
+               if (sscanf(line, "serial-number %u", &serial_number) == 1)
+                       strcpy(serial_line, line);
+               if (!strncmp(line, "software-version", 16))
+                       break;
+       }
+       if (!serial_number) {
+               fprintf(stderr, "no serial number found\n");
+               cc_usb_close(cc);
+               exit(1);
+       }
+       printf ("Serial number: %d\n", serial_number);
+       done = 0;
+       column = 0;
+       for (block = 0; !done && block < 511; block++) {
+               cc_usb_printf(cc, "e %x\n", block);
+               if (column == 64) {
+                       putchar('\n');
+                       column = 0;
+               }
+               putchar('.'); fflush(stdout); column++;
+               any_valid = 0;
+               for (addr = 0; addr < 0x100;) {
+                       cc_usb_getline(cc, line, sizeof (line));
+                       if (sscanf(line, "00%x %x %x %x %x %x %x %x %x",
+                                         &received_addr,
+                                         &data[0], &data[1], &data[2], &data[3],
+                                         &data[4], &data[5], &data[6], &data[7]) == 9)
+                       {
+                               if (received_addr != addr)
+                                       fprintf(stderr, "data out of sync at 0x%x\n",
+                                               block * 256 + received_addr);
+
+                               if (log_checksum(data) != 0)
+                                       fprintf (stderr, "invalid checksum at 0x%x\n",
+                                                block * 256 + received_addr);
+                               else
+                                       any_valid = 1;
+
+                               cmd = data[0];
+                               tick = data[2] + (data[3] << 8);
+                               a = data[4] + (data[5] << 8);
+                               b = data[6] + (data[7] << 8);
+                               if (cmd == 'F') {
+                                       flight = b;
+                                       filename = cc_make_filename(serial_number, flight, "eeprom");
+                                       printf ("Flight:       %d\n", flight);
+                                       printf ("File name:     %s\n", filename);
+                                       out = fopen (filename, "w");
+                                       if (!out) {
+                                               perror(filename);
+                                               exit(1);
+                                       }
+                                       fprintf(out, "%s\n", serial_line);
+                               }
+
+                               if (cmd == 'S' && a <= 8) {
+                                       if (column) putchar('\n');
+                                       printf("%s\n", state_names[a]);
+                                       column = 0;
+                               }
+                               if (out) {
+                                       fprintf(out, "%c %4x %4x %4x\n",
+                                               cmd, tick, a, b);
+                                       if (cmd == 'S' && a == 8) {
+                                               fclose(out);
+                                               out = NULL;
+                                               done = 1;
+                                       }
+                               }
+                               addr += 8;
+                       }
+               }
+               if (!any_valid) {
+                       fclose(out);
+                       out = NULL;
+                       done = 1;
+               }
+       }
+       if (column)
+               putchar('\n');
+       if (out)
+               fclose (out);
+       cc_usb_close(cc);
+       exit (0);
+}
index 8caff9d1a1510efd51edc1fb24d8b48cf45dd3c2..ed498147fed27a007dadf0f23842539449db85e3 100644 (file)
 ao-eeprom \- Fetch eeprom contents from TeleMetrum device
 .SH SYNOPSIS
 .B "ao-eeprom"
-[\-tty \fItty-device\fP]
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device the debugger uses to communicate with
+the target device. The special name 'BITBANG' directs ao-dbg to use
+the cp2103 connection, otherwise this should be a usb serial port
+connected to a suitable cc1111 debug node.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMetrum:2
+.br
+TeleMetrum
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
 .SH DESCRIPTION
 .I ao-eeprom
 downloads the eeprom contents from a connected TeleMetrum device.
index 726cc22cafd0e0c1a8becbe4ef52ab03c5e1b547..b865e298d2e01b6a499c6a7e270e46a682d31e2b 100644 (file)
 #include <unistd.h>
 #include <getopt.h>
 #include "cc-usb.h"
+#include "cc.h"
 
 #define NUM_BLOCK      512
 
 static const struct option options[] = {
        { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
        { 0, 0, 0, 0},
 };
 
 static void usage(char *program)
 {
-       fprintf(stderr, "usage: %s [--tty <tty-name>]\n", program);
+       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>\n", program);
        exit(1);
 }
 
@@ -40,23 +42,29 @@ main (int argc, char **argv)
 {
        struct cc_usb   *cc;
        int             block;
-       uint8_t         bytes[32 * (2 + 8)];
+       uint8_t         bytes[2 * 32 * (2 + 8)];
        uint8_t         *b;
        int             i, j;
        uint32_t        addr;
        char            *tty = NULL;
+       char            *device = NULL;
        int             c;
 
-       while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) {
                switch (c) {
                case 'T':
                        tty = optarg;
                        break;
+               case 'D':
+                       device = optarg;
+                       break;
                default:
                        usage(argv[0]);
                        break;
                }
        }
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "TeleMetrum");
        if (!tty)
                tty = getenv("ALTOS_TTY");
        if (!tty)
@@ -64,11 +72,11 @@ main (int argc, char **argv)
        cc = cc_usb_open(tty);
        if (!cc)
                exit(1);
-       for (block = 0; block < NUM_BLOCK; block++) {
+       for (block = 0; block < NUM_BLOCK; block += 2) {
                cc_queue_read(cc, bytes, sizeof (bytes));
-               cc_usb_printf(cc, "e %x\n", block);
+               cc_usb_printf(cc, "e %x\ne %x\n", block, block + 1);
                cc_usb_sync(cc);
-               for (i = 0; i < 32; i++) {
+               for (i = 0; i < 32 * 2; i++) {
                        b = bytes + (i * 10);
                        addr = block * 256 + i * 8;
                        printf ("%06x", addr);
diff --git a/ao-tools/ao-list/Makefile.am b/ao-tools/ao-list/Makefile.am
new file mode 100644 (file)
index 0000000..de3c4de
--- /dev/null
@@ -0,0 +1,12 @@
+bin_PROGRAMS=ao-list
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+AO_LIST_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_list_DEPENDENCIES = $(AO_LIST_LIBS)
+
+ao_list_LDADD=$(AO_LIST_LIBS) $(LIBUSB_LIBS)
+
+ao_list_SOURCES = ao-list.c
+
+man_MANS = ao-list.1
diff --git a/ao-tools/ao-list/ao-list.1 b/ao-tools/ao-list/ao-list.1
new file mode 100644 (file)
index 0000000..03968c2
--- /dev/null
@@ -0,0 +1,32 @@
+/.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-LIST 1 "ao-list" ""
+.SH NAME
+ao-list \- List connected AltOS devices
+.SH SYNOPSIS
+.B "ao-list"
+.SH DESCRIPTION
+.I ao-list
+scans the attached USB devices, locates those running AltOS and
+displays their product name and serial number along with the tty
+device associated with the serial port over USB provided by AltOS.
+.SH USAGE
+.I ao-list
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-list/ao-list.c b/ao-tools/ao-list/ao-list.c
new file mode 100644 (file)
index 0000000..c4b43d8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "cc.h"
+
+int
+main (int argc, char **argv)
+{
+       struct cc_usbdevs       *devs;
+       struct cc_usbdev        *dev;
+       int                     i;
+
+       devs = cc_usbdevs_scan();
+       if (devs) {
+               for (i = 0; i < devs->ndev; i++) {
+                       dev = devs->dev[i];
+                       printf ("%-20.20s %6d %s\n",
+                               dev->product, dev->serial, dev->tty);
+               }
+               cc_usbdevs_free(devs);
+       }
+       return 0;
+}
index 10484f3bc4eb47d6d30aa1c455e3e35a445590e5..79b76a79bf868a2ba69150b1568cc1c998071d8d 100644 (file)
 ao-load \- flash a program to a AltOS device
 .SH SYNOPSIS
 .B "ao-load"
-[\-tty \fItty-device\fP]
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+[\--cal \fIradio-calibration\fP]
 \fIfile.ihx\fP
 \fIdevice serial number\fP
 .SH DESCRIPTION
 .I ao-load
 loads the specified .ihx file into the target device flash memory,
 customizing the AltOS image with the specified serial number.
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device the debugger uses to communicate with
+the target device. The special name 'BITBANG' directs ao-dbg to use
+the cp2103 connection, otherwise this should be a usb serial port
+connected to a suitable cc1111 debug node.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMetrum:2
+.br
+TeleMetrum
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
+.TP
+\-c radio-calibration | --cal radio-calibration
+This programs the radio calibration value into the image for hardware
+which doesn't have any eeprom storage for this value. The value here
+can be computed given the current radio calibration value, the
+measured frequency and the desired frequency:
+.IP
+       cal' = cal * (desired/measured)
+.IP
+The default calibration value is 1186611.
 .SH USAGE
 .I ao-load
 reads the specified .ihx file into memory, locates the matching .map
index c27fcbe9c4d191bcb3005f9e7d2964e2eaba3c00..e3cef4a5664f61c8d06959482dc48db8a342a08d 100644 (file)
 #include <unistd.h>
 #include <getopt.h>
 #include "ccdbg.h"
+#include "cc.h"
 
 #define AO_USB_DESC_STRING             3
 
 struct sym {
        unsigned        addr;
        char            *name;
-} serial_symbols[] = {
-       { 0,    "_ao_serial_number" },
-#define AO_SERIAL_NUMBER       (serial_symbols[0].addr)
-       { 0,    "_ao_usb_descriptors" },
-#define AO_USB_DESCRIPTORS     (serial_symbols[1].addr)
+       int             required;
+} ao_symbols[] = {
+       { 0,    "_ao_serial_number", 1 },
+#define AO_SERIAL_NUMBER       (ao_symbols[0].addr)
+       { 0,    "_ao_usb_descriptors", 0 },
+#define AO_USB_DESCRIPTORS     (ao_symbols[1].addr)
+       { 0,    "_ao_radio_cal", 1 },
+#define AO_RADIO_CAL           (ao_symbols[2].addr)
 };
 
-#define NUM_SERIAL_SYMBOLS     (sizeof(serial_symbols)/sizeof(serial_symbols[0]))
+#define NUM_SYMBOLS            3
+#define NUM_REQUIRED_SYMBOLS   2
 
 static int
 find_symbols(FILE *map)
@@ -47,7 +52,7 @@ find_symbols(FILE *map)
        char    *colon;
        unsigned long   a;
        int     s;
-       int     found = 0;
+       int     required = 0;
 
        while (fgets(line, sizeof(line), map) != NULL) {
                line[sizeof(line)-1] = '\0';
@@ -63,14 +68,15 @@ find_symbols(FILE *map)
                a = strtoul(colon+1, &addr_end, 16);
                if (a == ULONG_MAX || addr_end == addr)
                        continue;
-               for (s = 0; s < NUM_SERIAL_SYMBOLS; s++)
-                       if (!strcmp(serial_symbols[s].name, name)) {
-                               serial_symbols[s].addr = (unsigned) a;
-                               ++found;
+               for (s = 0; s < NUM_SYMBOLS; s++)
+                       if (!strcmp(ao_symbols[s].name, name)) {
+                               ao_symbols[s].addr = (unsigned) a;
+                               if (ao_symbols[s].required)
+                                       ++required;
                                break;
                        }
        }
-       return found == NUM_SERIAL_SYMBOLS;
+       return required >= NUM_REQUIRED_SYMBOLS;
 }
 
 static int
@@ -91,12 +97,14 @@ rewrite(struct hex_image *image, unsigned addr, char *data, int len)
 
 static const struct option options[] = {
        { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "cal", .has_arg = 1, .val = 'c' },
        { 0, 0, 0, 0},
 };
 
 static void usage(char *program)
 {
-       fprintf(stderr, "usage: %s [--tty <tty-name>] file.ihx serial-number\n", program);
+       fprintf(stderr, "usage: %s [--tty=<tty-name>] [--device=<device-name>] [--cal=<radio-cal>] file.ihx serial-number\n", program);
        exit(1);
 }
 
@@ -119,16 +127,27 @@ main (int argc, char **argv)
        char            serial_int[2];
        unsigned int    s;
        int             i;
-       unsigned        usb_descriptors;
        int             string_num;
        char            *tty = NULL;
+       char            *device = NULL;
+       uint32_t        cal = 0;
+       char            cal_int[4];
+       char            *cal_end;
        int             c;
 
-       while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "T:D:c:", options, NULL)) != -1) {
                switch (c) {
                case 'T':
                        tty = optarg;
                        break;
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'c':
+                       cal = strtoul(optarg, &cal_end, 10);
+                       if (cal_end == optarg || *cal_end != '\0')
+                               usage(argv[0]);
+                       break;
                default:
                        usage(argv[0]);
                        break;
@@ -178,7 +197,7 @@ main (int argc, char **argv)
 
        serial = strtoul(serial_string, NULL, 0);
        if (!serial)
-(argv[0]);
+               usage(argv[0]);
 
        serial_int[0] = serial & 0xff;
        serial_int[1] = (serial >> 8) & 0xff;
@@ -189,36 +208,55 @@ main (int argc, char **argv)
                exit(1);
        }
 
-       usb_descriptors = AO_USB_DESCRIPTORS - image->address;
-       string_num = 0;
-       while (image->data[usb_descriptors] != 0 && usb_descriptors < image->length) {
-               if (image->data[usb_descriptors+1] == AO_USB_DESC_STRING) {
-                       ++string_num;
-                       if (string_num == 4)
-                               break;
+       if (AO_USB_DESCRIPTORS) {
+               unsigned        usb_descriptors;
+               usb_descriptors = AO_USB_DESCRIPTORS - image->address;
+               string_num = 0;
+               while (image->data[usb_descriptors] != 0 && usb_descriptors < image->length) {
+                       if (image->data[usb_descriptors+1] == AO_USB_DESC_STRING) {
+                               ++string_num;
+                               if (string_num == 4)
+                                       break;
+                       }
+                       usb_descriptors += image->data[usb_descriptors];
+               }
+               if (usb_descriptors >= image->length || image->data[usb_descriptors] == 0 ) {
+                       fprintf(stderr, "Cannot rewrite serial string at %04x\n", AO_USB_DESCRIPTORS);
+                       exit(1);
                }
-               usb_descriptors += image->data[usb_descriptors];
-       }
-       if (usb_descriptors >= image->length || image->data[usb_descriptors] == 0 ) {
-               fprintf(stderr, "Cannot rewrite serial string at %04x\n", AO_USB_DESCRIPTORS);
-               exit(1);
-       }
 
-       serial_ucs2_len = image->data[usb_descriptors] - 2;
-       serial_ucs2 = malloc(serial_ucs2_len);
-       if (!serial_ucs2) {
-               fprintf(stderr, "Malloc(%d) failed\n", serial_ucs2_len);
-               exit(1);
-       }
-       s = serial;
-       for (i = serial_ucs2_len / 2; i; i--) {
-               serial_ucs2[i * 2 - 1] = 0;
-               serial_ucs2[i * 2 - 2] = (s % 10) + '0';
-               s /= 10;
+               serial_ucs2_len = image->data[usb_descriptors] - 2;
+               serial_ucs2 = malloc(serial_ucs2_len);
+               if (!serial_ucs2) {
+                       fprintf(stderr, "Malloc(%d) failed\n", serial_ucs2_len);
+                       exit(1);
+               }
+               s = serial;
+               for (i = serial_ucs2_len / 2; i; i--) {
+                       serial_ucs2[i * 2 - 1] = 0;
+                       serial_ucs2[i * 2 - 2] = (s % 10) + '0';
+                       s /= 10;
+               }
+               if (!rewrite(image, usb_descriptors + 2 + image->address, serial_ucs2, serial_ucs2_len))
+                       usage(argv[0]);
        }
-       if (!rewrite(image, usb_descriptors + 2 + image->address, serial_ucs2, serial_ucs2_len))
-               usage(argv[0]);
 
+       if (cal) {
+               cal_int[0] = cal & 0xff;
+               cal_int[1] = (cal >> 8) & 0xff;
+               cal_int[2] = (cal >> 16) & 0xff;
+               cal_int[3] = (cal >> 24) & 0xff;
+               if (!AO_RADIO_CAL) {
+                       fprintf(stderr, "Cannot find radio calibration location in image\n");
+                       exit(1);
+               }
+               if (!rewrite(image, AO_RADIO_CAL, cal_int, sizeof (cal_int))) {
+                       fprintf(stderr, "Cannot rewrite radio calibration at %04x\n", AO_RADIO_CAL);
+                       exit(1);
+               }
+       }
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "TIDongle");
        dbg = ccdbg_open(tty);
        if (!dbg)
                exit (1);
diff --git a/ao-tools/ao-postflight/Makefile.am b/ao-tools/ao-postflight/Makefile.am
new file mode 100644 (file)
index 0000000..589d164
--- /dev/null
@@ -0,0 +1,12 @@
+bin_PROGRAMS=ao-postflight
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS) $(GNOME_CFLAGS) $(PLPLOT_CFLAGS)
+AO_POSTFLIGHT_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_postflight_DEPENDENCIES = $(AO_POSTFLIGHT_LIBS)
+
+ao_postflight_LDADD=$(AO_POSTFLIGHT_LIBS) $(LIBUSB_LIBS) $(GNOME_LIBS) $(PLPLOT_LIBS)
+
+ao_postflight_SOURCES = ao-postflight.c
+
+man_MANS = ao-postflight.1
diff --git a/ao-tools/ao-postflight/ao-postflight.1 b/ao-tools/ao-postflight/ao-postflight.1
new file mode 100644 (file)
index 0000000..7bafb6e
--- /dev/null
@@ -0,0 +1,61 @@
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-POSTFLIGHT 1 "ao-postflight" ""
+.SH NAME
+ao-postflight \- Analyse a flight log (either telemetry or eeprom)
+.SH SYNOPSIS
+.B "ao-postflight"
+[\-s <summary-file>]
+[\--summary=<summary-file>]
+[\-d <detail-file>]
+[\--detail=<detail-file>]
+[\-r <raw-file>]
+[\--raw=<raw-file>]
+[\-p <plot-file>]
+[\--plot=<plot-file>]
+[\-g <gps-file]
+[\--gps=<gps-file]
+[\-k <kml-file]
+[\--kml=<kml-file]
+{flight.eeprom|flight.telem}
+.SH DESCRIPTION
+.I ao-postflight
+reads the specified flight log and produces several different kinds of
+output.
+.IP Summary
+By default, summary information is shown on stdout. With the --summary
+option, it can be redirected to a file.
+.IP Detail
+When requested with the --detail option, a filtered version of the
+flight position, speed and acceleration are written to the specified
+file.
+.IP Raw
+The --raw option writes the unfiltered, but converted acceleration
+and height data to the specified file.
+.IP Plot
+The --plot option writes plots of height, speed and acceleration to
+the specified file in .svg format
+.IP GPS
+The --gps option writes the recorded GPS data to the specified file in
+three columns.
+.IP KML
+The --kml option writes the recorded GPS data to the specified file in
+Keyhole Markup Language format, which can be displayed in Googleearth.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-postflight/ao-postflight.c b/ao-tools/ao-postflight/ao-postflight.c
new file mode 100644 (file)
index 0000000..e5b5566
--- /dev/null
@@ -0,0 +1,827 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "cc-usb.h"
+#include "cc.h"
+#include <plplot/plplot.h>
+
+static const char *state_names[] = {
+       "startup",
+       "idle",
+       "pad",
+       "boost",
+       "fast",
+       "coast",
+       "drogue",
+       "main",
+       "landed",
+       "invalid"
+};
+
+static const char *kml_state_colours[] = {
+       "FF000000",
+       "FF000000",
+       "FF000000",
+       "FF0000FF",
+       "FF4080FF",
+       "FF00FFFF",
+       "FFFF0000",
+       "FF00FF00",
+       "FF000000",
+       "FFFFFFFF"
+};
+
+static int plot_colors[3][3] = {
+       { 0, 0x90, 0 }, /* height */
+       { 0xa0, 0, 0 }, /* speed */
+       { 0, 0, 0xc0 }, /* accel */
+};
+
+#define PLOT_HEIGHT    0
+#define PLOT_SPEED     1
+#define PLOT_ACCEL     2
+
+static void
+plot_perioddata(struct cc_perioddata *d, char *axis_label, char *plot_label,
+               double min_time, double max_time, int plot_type)
+{
+       double  *times;
+       double  ymin, ymax;
+       int     ymin_i, ymax_i;
+       int     i;
+       int     start, stop;
+
+       if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop))
+               return;
+
+       times = calloc(stop - start + 1, sizeof (double));
+       for (i = start; i <= stop; i++)
+               times[i-start] = i * d->step / 100.0;
+
+       ymin_i = cc_perioddata_min(d, min_time, max_time);
+       ymax_i = cc_perioddata_max(d, min_time, max_time);
+       ymin = d->data[ymin_i];
+       ymax = d->data[ymax_i];
+       plscol0(1, 0, 0, 0);
+       plscol0(2, plot_colors[plot_type][0],  plot_colors[plot_type][1],  plot_colors[plot_type][2]);
+       plcol0(1);
+       plenv(times[0], times[stop-start],
+             ymin, ymax, 0, 2);
+       pllab("Time", axis_label, plot_label);
+       plcol0(2);
+       plline(stop - start + 1, times, d->data + start);
+       free(times);
+}
+
+static void
+plot_timedata(struct cc_timedata *d, char *axis_label, char *plot_label,
+             double min_time, double max_time, int plot_type)
+{
+       double  *times;
+       double  *values;
+       double  ymin, ymax;
+       int     ymin_i, ymax_i;
+       int     i;
+       int     start = -1, stop = -1;
+       double  start_time = 0, stop_time = 0;
+       int     num;
+
+       for (i = 0; i < d->num; i++) {
+               if (start < 0 && d->data[i].time >= min_time) {
+                       start_time = d->data[i].time;
+                       start = i;
+               }
+               if (d->data[i].time <= max_time) {
+                       stop_time = d->data[i].time;
+                       stop = i;
+               }
+       }
+
+       times = calloc(stop - start + 1, sizeof (double));
+       values = calloc(stop - start + 1, sizeof (double));
+
+       ymin_i = cc_timedata_min(d, min_time, max_time);
+       ymax_i = cc_timedata_max(d, min_time, max_time);
+       ymin = d->data[ymin_i].value;
+       ymax = d->data[ymax_i].value;
+       for (i = start; i <= stop; i++) {
+               times[i-start] = (d->data[i].time - start_time)/100.0;
+               values[i-start] = d->data[i].value;
+       }
+       plscol0(1, 0, 0, 0);
+       plscol0(2, plot_colors[plot_type][0],  plot_colors[plot_type][1],  plot_colors[plot_type][2]);
+       plcol0(1);
+       plenv(times[0], times[stop-start], ymin, ymax, 0, 2);
+       pllab("Time", axis_label, plot_label);
+       plcol0(2);
+       plline(stop - start + 1, times, values);
+       free(times);
+       free(values);
+}
+
+static struct cc_perioddata *
+merge_data(struct cc_perioddata *first, struct cc_perioddata *last, double split_time)
+{
+       int                     i;
+       struct cc_perioddata    *pd;
+       int                     num;
+       double                  start_time, stop_time;
+       double                  t;
+
+       pd = calloc(1, sizeof (struct cc_perioddata));
+       start_time = first->start;
+       stop_time = last->start + last->step * last->num;
+       num = (stop_time - start_time) / first->step;
+       pd->num = num;
+       pd->data = calloc(num, sizeof (double));
+       pd->start = first->start;
+       pd->step = first->step;
+       for (i = 0; i < num; i++) {
+               t = pd->start + i * pd->step;
+               if (t <= split_time) {
+                       pd->data[i] = first->data[i];
+               } else {
+                       int     j;
+
+                       j = (t - last->start) / last->step;
+                       if (j < 0 || j >= last->num)
+                               pd->data[i] = 0;
+                       else
+                               pd->data[i] = last->data[j];
+               }
+       }
+       return pd;
+}
+
+static const char kml_header_start[] =
+       "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+       "<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n"
+       "<Document>\n"
+       "  <name>%s</name>\n"
+       "  <description>\n";
+static const char kml_header_end[] =
+       "  </description>\n"
+       "  <open>0</open>\n";
+
+static const char kml_style_start[] =
+       "  <Style id=\"ao-flightstate-%s\">\n"
+       "    <LineStyle><color>%s</color><width>4</width></LineStyle>\n"
+       "    <BalloonStyle>\n"
+       "      <text>\n";
+static const char kml_style_end[] =
+       "      </text>\n"
+       "    </BalloonStyle>\n"
+       "  </Style>\n";
+
+static const char kml_placemark_start[] =
+       "  <Placemark>\n"
+       "    <name>%s</name>\n"
+       "    <styleUrl>#ao-flightstate-%s</styleUrl>\n"
+       "    <LineString>\n"
+       "      <tessellate>1</tessellate>\n"
+       "      <altitudeMode>absolute</altitudeMode>\n"
+       "      <coordinates>\n";
+
+static const char kml_coord_fmt[] =
+       "        %12.7f, %12.7f, %12.7f <!-- alt %12.7f time %12.7f sats %d -->\n";
+
+static const char kml_placemark_end[] =
+       "      </coordinates>\n"
+       "    </LineString>\n"
+       "  </Placemark>\n";
+
+static const char kml_footer[] =
+       "      </coordinates>\n"
+       "    </LineString>\n"
+       "  </Placemark>\n"
+       "</Document>\n"
+       "</kml>\n";
+
+static unsigned
+gps_daytime(struct cc_gpselt *gps)
+{
+       return ((gps->hour * 60 +
+                gps->minute) * 60 +
+               gps->second) * 1000;
+}
+
+int
+daytime_hour(unsigned daytime)
+{
+       return daytime / 1000 / 60 / 60;
+}
+
+int
+daytime_minute(unsigned daytime)
+{
+       return (daytime / 1000 / 60) % 60;
+}
+
+int
+daytime_second(unsigned daytime)
+{
+       return (daytime / 1000) % 60;
+}
+
+int
+daytime_millisecond(unsigned daytime)
+{
+       return daytime % 1000;
+}
+
+static unsigned
+compute_daytime_ms(double time, struct cc_gpsdata *gps)
+{
+       int     i;
+       unsigned        gps_start_daytime, gps_stop_daytime;
+
+       if (time <= gps->data[0].time) {
+               gps_stop_daytime = gps_daytime(&gps->data[0]);
+               return gps_stop_daytime - (gps->data[0].time - time) * 10;
+       }
+       for (i = 0; i < gps->num - 1; i++)
+               if (time > gps->data[i].time)
+                       break;
+       gps_start_daytime = gps_daytime(&gps->data[i]);
+       if (i == gps->num - 1) {
+               return gps_start_daytime + (time - gps->data[i].time) * 10;
+       } else {
+               unsigned        gps_period_daytime;
+               double          gps_period_time;
+               double          time_since_start;
+
+               gps_stop_daytime = gps_daytime(&gps->data[i + 1]);
+
+               /* range of gps daytime values */
+               gps_period_daytime = gps_stop_daytime - gps_start_daytime;
+
+               /* range of gps time values */
+               gps_period_time = gps->data[i+1].time - gps->data[i].time;
+
+               /* sample time after first gps time */
+               time_since_start = time - gps->data[i].time;
+
+               return gps_start_daytime +
+                       gps_period_daytime * time_since_start / gps_period_time;
+       }
+}
+
+static void
+analyse_flight(struct cc_flightraw *f, FILE *summary_file, FILE *detail_file,
+              FILE *raw_file, char *plot_name, FILE *gps_file, FILE *kml_file)
+{
+       double  height;
+       double  accel;
+       double  speed;
+       double  avg_speed;
+       double  boost_start, boost_stop;
+       double  min_pres;
+       int     i;
+       int     pres_i, accel_i, speed_i;
+       int     boost_start_set = 0;
+       int     boost_stop_set = 0;
+       enum ao_flight_state    state;
+       double  state_start, state_stop;
+       struct cc_flightcooked *cooked;
+       double  apogee;
+       char    buf[128];
+
+       if (kml_file) {
+                snprintf(buf, sizeof (buf), "AO Flight#%d S/N: %03d", f->flight, f->serial);
+               fprintf(kml_file, kml_header_start, buf);
+       }
+
+       fprintf(summary_file,
+               "Serial:  %9d\n"
+               "Flight:  %9d\n",
+               f->serial, f->flight);
+
+       if (f->year) {
+               snprintf(buf, sizeof (buf),
+                       "Date:   %04d-%02d-%02d\n",
+                       f->year, f->month, f->day);
+               fprintf(summary_file, "%s", buf);
+               if (kml_file) fprintf(kml_file, "%s", buf);
+       }
+       if (f->gps.num) {
+               snprintf(buf, sizeof (buf),
+                       "Time:     %2d:%02d:%02d\n",
+                       f->gps.data[0].hour,
+                       f->gps.data[0].minute,
+                       f->gps.data[0].second);
+               fprintf(summary_file, "%s", buf);
+               if (kml_file) fprintf(kml_file, "%s", buf);
+       }
+       boost_start = f->accel.data[0].time;
+       boost_stop = f->accel.data[f->accel.num-1].time;
+       for (i = 0; i < f->state.num; i++) {
+               if (f->state.data[i].value == ao_flight_boost && !boost_start_set) {
+                       boost_start = f->state.data[i].time;
+                       boost_start_set = 1;
+               }
+               if (f->state.data[i].value > ao_flight_boost && !boost_stop_set) {
+                       boost_stop = f->state.data[i].time;
+                       boost_stop_set = 1;
+               }
+       }
+
+       pres_i = cc_timedata_min(&f->pres, f->pres.data[0].time,
+                                f->pres.data[f->pres.num-1].time);
+       if (pres_i >= 0)
+       {
+               min_pres = f->pres.data[pres_i].value;
+               height = cc_barometer_to_altitude(min_pres) -
+                       cc_barometer_to_altitude(f->ground_pres);
+               apogee = f->pres.data[pres_i].time;
+               snprintf(buf, sizeof (buf), "Max height: %9.2fm    %9.2fft   %9.2fs\n",
+                       height, height * 100 / 2.54 / 12,
+                       (f->pres.data[pres_i].time - boost_start) / 100.0);
+               fprintf(summary_file, "%s", buf);
+               if (kml_file) fprintf(kml_file, "%s", buf);
+       }
+
+       cooked = cc_flight_cook(f);
+       if (cooked) {
+               speed_i = cc_perioddata_max(&cooked->accel_speed, boost_start, boost_stop);
+               if (speed_i >= 0) {
+                       speed = cooked->accel_speed.data[speed_i];
+                       snprintf(buf, sizeof (buf), "Max speed:  %9.2fm/s  %9.2fft/s %9.2fs\n",
+                              speed, speed * 100 / 2.4 / 12.0,
+                              (cooked->accel_speed.start + speed_i * cooked->accel_speed.step - boost_start) / 100.0);
+                       fprintf(summary_file, "%s", buf);
+                       if (kml_file) fprintf(kml_file, "%s", buf);
+               }
+       }
+       accel_i = cc_timedata_min(&f->accel, boost_start, boost_stop);
+       if (accel_i >= 0)
+       {
+               accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value,
+                                                        f->ground_accel);
+               snprintf(buf, sizeof (buf), "Max accel:  %9.2fm/s² %9.2fg    %9.2fs\n",
+                       accel, accel /  9.80665,
+                       (f->accel.data[accel_i].time - boost_start) / 100.0);
+               fprintf(summary_file, "%s", buf);
+               if (kml_file) fprintf(kml_file, "%s", buf);
+       }
+
+       if (kml_file)
+               fprintf(kml_file, "%s", kml_header_end);
+
+       for (i = 0; i < f->state.num; i++) {
+               state = f->state.data[i].value;
+               state_start = f->state.data[i].time;
+               while (i < f->state.num - 1 && f->state.data[i+1].value == state)
+                       i++;
+               if (i < f->state.num - 1)
+                       state_stop = f->state.data[i + 1].time;
+               else
+                       state_stop = f->accel.data[f->accel.num-1].time;
+               fprintf(summary_file, "State: %s\n", state_names[state]);
+               fprintf(summary_file, "\tStart:      %9.2fs\n", (state_start - boost_start) / 100.0);
+               fprintf(summary_file, "\tDuration:   %9.2fs\n", (state_stop - state_start) / 100.0);
+               if (kml_file) {
+                       fprintf(kml_file, kml_style_start, state_names[state], kml_state_colours[state]);
+                       fprintf(kml_file, "\tState: %s\n", state_names[state]);
+                       fprintf(kml_file, "\tStart:      %9.2fs\n", (state_start - boost_start) / 100.0);
+                       fprintf(kml_file, "\tDuration:   %9.2fs\n", (state_stop - state_start) / 100.0);
+               }
+
+               accel_i = cc_timedata_min(&f->accel, state_start, state_stop);
+               if (accel_i >= 0)
+               {
+                       accel = cc_accelerometer_to_acceleration(f->accel.data[accel_i].value,
+                                                                f->ground_accel);
+                       snprintf(buf, sizeof (buf), "\tMax accel:  %9.2fm/s² %9.2fg    %9.2fs\n",
+                              accel, accel / 9.80665,
+                              (f->accel.data[accel_i].time - boost_start) / 100.0);
+                       fprintf(summary_file, "%s", buf);
+                       if (kml_file) fprintf(kml_file, "%s", buf);
+               }
+
+               if (cooked) {
+                       if (state < ao_flight_drogue) {
+                               speed_i = cc_perioddata_max_mag(&cooked->accel_speed, state_start, state_stop);
+                               if (speed_i >= 0)
+                                       speed = cooked->accel_speed.data[speed_i];
+                               avg_speed = cc_perioddata_average(&cooked->accel_speed, state_start, state_stop);
+                       } else {
+                               speed_i = cc_perioddata_max_mag(&cooked->pres_speed, state_start, state_stop);
+                               if (speed_i >= 0)
+                                       speed = cooked->pres_speed.data[speed_i];
+                               avg_speed = cc_perioddata_average(&cooked->pres_speed, state_start, state_stop);
+                       }
+                       if (speed_i >= 0)
+                       {
+                               snprintf(buf, sizeof (buf), "\tMax speed:  %9.2fm/s  %9.2fft/s %9.2fs\n",
+                                      speed, speed * 100 / 2.4 / 12.0,
+                                      (cooked->accel_speed.start + speed_i * cooked->accel_speed.step - boost_start) / 100.0);
+                               fprintf(summary_file, "%s", buf);
+                               if (kml_file) fprintf(kml_file, "%s", buf);
+
+                               snprintf(buf, sizeof (buf), "\tAvg speed:  %9.2fm/s  %9.2fft/s\n",
+                                       avg_speed, avg_speed * 100 / 2.4 / 12.0);
+                               fprintf(summary_file, "%s", buf);
+                               if (kml_file) fprintf(kml_file, "%s", buf);
+                       }
+               }
+               pres_i = cc_timedata_min(&f->pres, state_start, state_stop);
+               if (pres_i >= 0)
+               {
+                       min_pres = f->pres.data[pres_i].value;
+                       height = cc_barometer_to_altitude(min_pres) -
+                               cc_barometer_to_altitude(f->ground_pres);
+                       snprintf(buf, sizeof (buf), "\tMax height: %9.2fm    %9.2fft   %9.2fs\n",
+                               height, height * 100 / 2.54 / 12,
+                               (f->pres.data[pres_i].time - boost_start) / 100.0);
+                       fprintf(summary_file, "%s", buf);
+                       if (kml_file) fprintf(kml_file, "%s", buf);
+               }
+               if (kml_file) fprintf(kml_file, "%s", kml_style_end);
+       }
+       if (cooked && detail_file) {
+               double  max_height = 0;
+               int     i;
+               double  *times;
+
+               fprintf(detail_file, "%9s %9s %9s %9s %9s\n",
+                       "time", "height", "speed", "accel", "daytime");
+               for (i = 0; i < cooked->pres_pos.num; i++) {
+                       double  clock_time = cooked->accel_accel.start + i * cooked->accel_accel.step;
+                       double  time = (clock_time - boost_start) / 100.0;
+                       double  accel = cooked->accel_accel.data[i];
+                       double  pos = cooked->pres_pos.data[i];
+                       double  speed;
+                       unsigned        daytime;
+                       if (cooked->pres_pos.start + cooked->pres_pos.step * i < apogee)
+                               speed = cooked->accel_speed.data[i];
+                       else
+                               speed = cooked->pres_speed.data[i];
+                       if (f->gps.num)
+                               daytime = compute_daytime_ms(clock_time, &f->gps);
+                       else
+                               daytime = 0;
+                       fprintf(detail_file, "%9.2f %9.2f %9.2f %9.2f %02d:%02d:%02d.%03d\n",
+                               time, pos, speed, accel,
+                               daytime_hour(daytime),
+                               daytime_minute(daytime),
+                               daytime_second(daytime),
+                               daytime_millisecond(daytime));
+               }
+       }
+       if (raw_file) {
+               fprintf(raw_file, "%9s %9s %9s %9s\n",
+                       "time", "height", "accel", "daytime");
+               for (i = 0; i < cooked->pres.num; i++) {
+                       double time = cooked->pres.data[i].time;
+                       double pres = cooked->pres.data[i].value;
+                       double accel = cooked->accel.data[i].value;
+                       unsigned        daytime;
+                       if (f->gps.num)
+                               daytime = compute_daytime_ms(time, &f->gps);
+                       else
+                               daytime = 0;
+                       fprintf(raw_file, "%9.2f %9.2f %9.2f %02d:%02d:%02d.%03d\n",
+                               time, pres, accel,
+                               daytime_hour(daytime),
+                               daytime_minute(daytime),
+                               daytime_second(daytime),
+                               daytime_millisecond(daytime));
+               }
+       }
+       if (gps_file || kml_file) {
+               int     j = 0, baro_pos;
+               double  baro_offset;
+               double  baro = 0.0;
+               int     state_idx = 0;
+
+               if (gps_file)
+                       fprintf(gps_file, "%2s %2s %2s %9s %12s %12s %9s %8s %5s\n",
+                               "hr", "mn", "sc",
+                               "time", "lat", "lon", "alt", "baro", "nsat");
+               if (kml_file)
+                       fprintf(kml_file, kml_placemark_start,
+                               state_names[(int)f->state.data[state_idx].value],
+                               state_names[(int)f->state.data[state_idx].value]); 
+
+               if (f->gps.num)
+                       baro_offset = f->gps.data[0].alt;
+               else
+                       baro_offset = 0;
+               baro_pos = 0;
+               for (i = 0; i < f->gps.num; i++) {
+                       int     nsat = 0;
+                       int     k;
+                       while (j < f->gps.numsats - 1) {
+                               if (f->gps.sats[j].sat[0].time <= f->gps.data[i].time &&
+                                   f->gps.data[i].time < f->gps.sats[j+1].sat[0].time)
+                                       break;
+                               j++;
+                       }
+                       if (cooked) {
+                               while (baro_pos < cooked->pres_pos.num) {
+                                       double  baro_time = cooked->accel_accel.start + baro_pos * cooked->accel_accel.step;
+                                       if (baro_time >= f->gps.data[i].time)
+                                               break;
+                                       baro_pos++;
+                               }
+                               if (baro_pos < cooked->pres_pos.num)
+                                       baro = cooked->pres_pos.data[baro_pos];
+                       }
+                       if (gps_file)
+                               fprintf(gps_file, "%2d %2d %2d %12.7f %12.7f %12.7f %7.1f %7.1f",
+                                       f->gps.data[i].hour,
+                                       f->gps.data[i].minute,
+                                       f->gps.data[i].second,
+                                       (f->gps.data[i].time - boost_start) / 100.0,
+                                       f->gps.data[i].lat,
+                                       f->gps.data[i].lon,
+                                       f->gps.data[i].alt,
+                                       baro + baro_offset);
+
+                       nsat = 0;
+                       if (f->gps.sats) {
+                               for (k = 0; k < f->gps.sats[j].nsat; k++) {
+                                       if (f->gps.sats[j].sat[k].svid != 0)
+                                               nsat++;
+                               }
+                               if (gps_file) {
+                                       fprintf(gps_file, " %4d", nsat);
+                                       for (k = 0; k < f->gps.sats[j].nsat; k++) {
+                                               if (f->gps.sats[j].sat[k].svid != 0) {
+                                                       fprintf (gps_file, " %3d(%4.1f)",
+                                                                f->gps.sats[j].sat[k].svid,
+                                                                (double) f->gps.sats[j].sat[k].c_n);
+                                               }
+                                       }
+                                       fprintf(gps_file, "\n");
+                               }
+                       }
+
+                       if (kml_file) {
+                               snprintf(buf, sizeof (buf), kml_coord_fmt,
+                                       f->gps.data[i].lon,
+                                       f->gps.data[i].lat,
+                                       baro + baro_offset,
+                                       f->gps.data[i].alt,
+                                       (f->gps.data[i].time - boost_start) / 100.0,
+                                       nsat);
+                               fprintf(kml_file, "%s", buf);
+                               if (state_idx + 1 < f->state.num && f->state.data[state_idx + 1].time <= f->gps.data[i].time) {
+                                       state_idx++;
+                                       if (f->state.data[state_idx - 1].value != f->state.data[state_idx].value) {
+                                               fprintf(kml_file, "%s", kml_placemark_end);
+                                               fprintf(kml_file, kml_placemark_start,
+                                                       state_names[(int)f->state.data[state_idx].value],
+                                                       state_names[(int)f->state.data[state_idx].value]); 
+                                               fprintf(kml_file, "%s", buf);
+                                       }
+                               }
+                       }
+
+               }
+               if (kml_file)
+                       fprintf(kml_file, "%s", kml_footer);
+       }
+       if (cooked && plot_name) {
+               struct cc_perioddata    *speed;
+               plsdev("svgcairo");
+               plsfnam(plot_name);
+#define PLOT_DPI       96
+               plspage(PLOT_DPI, PLOT_DPI, 8 * PLOT_DPI, 8 * PLOT_DPI, 0, 0);
+               plscolbg(0xff, 0xff, 0xff);
+               plscol0(1,0,0,0);
+               plstar(2, 3);
+               speed = merge_data(&cooked->accel_speed, &cooked->pres_speed, apogee);
+
+               plot_perioddata(&cooked->pres_pos, "meters", "Height",
+                               -1e10, 1e10, PLOT_HEIGHT);
+               plot_perioddata(&cooked->pres_pos, "meters", "Height to Apogee",
+                               boost_start, apogee + (apogee - boost_start) / 10.0, PLOT_HEIGHT);
+               plot_perioddata(speed, "meters/second", "Speed",
+                               -1e10, 1e10, PLOT_SPEED);
+               plot_perioddata(speed, "meters/second", "Speed to Apogee",
+                               boost_start, apogee + (apogee - boost_start) / 10.0, PLOT_SPEED);
+               plot_perioddata(&cooked->accel_accel, "meters/second²", "Acceleration",
+                               -1e10, 1e10, PLOT_ACCEL);
+/*             plot_perioddata(&cooked->accel_accel, "meters/second²", "Acceleration during Boost",
+               boost_start, boost_stop + (boost_stop - boost_start) / 2.0, PLOT_ACCEL); */
+               plot_timedata(&cooked->accel, "meters/second²", "Acceleration during Boost",
+                               boost_start, boost_stop + (boost_stop - boost_start) / 2.0, PLOT_ACCEL);
+               free(speed->data);
+               free(speed);
+               plend();
+       }
+       if (cooked)
+               cc_flightcooked_free(cooked);
+}
+
+static const struct option options[] = {
+       { .name = "summary", .has_arg = 2, .val = 's' },
+       { .name = "detail", .has_arg = 2, .val = 'd' },
+       { .name = "plot", .has_arg = 2, .val = 'p' },
+       { .name = "raw", .has_arg = 2, .val = 'r' },
+       { .name = "gps", .has_arg = 2, .val = 'g' },
+       { .name = "kml", .has_arg = 2, .val = 'k' },
+       { .name = "all", .has_arg = 0, .val = 'a' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s\n"
+               "\t[--all] [-a]\n"
+               "\t[--summary=<summary-file>] [-s <summary-file>]\n"
+               "\t[--detail=<detail-file] [-d <detail-file>]\n"
+               "\t[--raw=<raw-file> -r <raw-file]\n"
+               "\t[--plot=<plot-file> -p <plot-file>]\n"
+               "\t[--gps=<gps-file> -g <gps-file>]\n"
+               "\t[--kml=<kml-file> -k <kml-file>]\n"
+               "\t{flight-log} ...\n", program);
+       exit(1);
+}
+
+char *
+replace_extension(char *file, char *extension)
+{
+       char    *slash;
+       char    *dot;
+       char    *new;
+       int     newlen;
+
+       slash = strrchr(file, '/');
+       dot = strrchr(file, '.');
+       if (!dot || (slash && dot < slash))
+               dot = file + strlen(file);
+       newlen = (dot - file) + strlen (extension) + 1;
+       new = malloc (newlen);
+       strncpy (new, file, dot - file);
+       new[dot-file] = '\0';
+       strcat (new, extension);
+       return new;
+}
+
+FILE *
+open_output(char *outname, char *inname, char *extension)
+{
+       char    *o;
+       FILE    *out;
+
+       if (outname)
+               o = outname;
+       else
+               o = replace_extension(inname, extension);
+       out = fopen(o, "w");
+       if (!out) {
+               perror (o);
+               exit(1);
+       }
+       if (o != outname)
+               free(o);
+       return out;
+}
+
+int
+main (int argc, char **argv)
+{
+       FILE                    *file;
+       FILE                    *summary_file = NULL;
+       FILE                    *detail_file = NULL;
+       FILE                    *raw_file = NULL;
+       FILE                    *gps_file = NULL;
+       FILE                    *kml_file = NULL;
+       int                     i;
+       int                     ret = 0;
+       struct cc_flightraw     *raw;
+       int                     c;
+       int                     serial;
+       char                    *s;
+       char                    *summary_name = NULL;
+       char                    *detail_name = NULL;
+       char                    *raw_name = NULL;
+       char                    *plot_name = NULL;
+       char                    *gps_name = NULL;
+       char                    *kml_name = NULL;
+       int                     has_summary = 0;
+       int                     has_detail = 0;
+       int                     has_plot = 0;
+       int                     has_raw = 0;
+       int                     has_gps = 0;
+       int                     has_kml = 0;
+       char                    *this_plot_name = NULL;;
+
+       while ((c = getopt_long(argc, argv, "s:d:p:r:g:k:a", options, NULL)) != -1) {
+               switch (c) {
+               case 's':
+                       summary_name = optarg;
+                       has_summary = 1;
+                       break;
+               case 'd':
+                       detail_name = optarg;
+                       has_detail = 1;
+                       break;
+               case 'p':
+                       plot_name = optarg;
+                       has_plot = 1;
+                       break;
+               case 'r':
+                       raw_name = optarg;
+                       has_raw = 1;
+                       break;
+               case 'g':
+                       gps_name = optarg;
+                       has_gps = 1;
+                       break;
+               case 'k':
+                       kml_name = optarg;
+                       has_kml = 1;
+                       break;
+               case 'a':
+                       has_summary = has_detail = has_plot = has_raw = has_gps = has_kml = 1;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+       if (!has_summary)
+               summary_file = stdout;
+       for (i = optind; i < argc; i++) {
+               file = fopen(argv[i], "r");
+               if (!file) {
+                       perror(argv[i]);
+                       ret++;
+                       continue;
+               }
+               if (has_summary && !summary_file)
+                       summary_file = open_output(summary_name, argv[i], ".summary");
+               if (has_detail && !detail_file)
+                       detail_file = open_output(detail_name, argv[i], ".detail");
+               if (has_plot) {
+                       if (plot_name)
+                               this_plot_name = plot_name;
+                       else
+                               this_plot_name = replace_extension(argv[i], ".plot");
+               }
+               if (has_raw && !raw_file)
+                       raw_file = open_output(raw_name, argv[i], ".raw");
+               if (has_gps && !gps_file)
+                       gps_file = open_output(gps_name, argv[i], ".gps");
+               if (has_kml && !kml_file)
+                       kml_file = open_output(kml_name, argv[i], ".kml");
+               s = strstr(argv[i], "-serial-");
+               if (s)
+                       serial = atoi(s + 8);
+               else
+                       serial = 0;
+               raw = cc_log_read(file);
+               if (!raw) {
+                       perror(argv[i]);
+                       ret++;
+                       continue;
+               }
+               if (!raw->serial)
+                       raw->serial = serial;
+               analyse_flight(raw, summary_file, detail_file, raw_file, this_plot_name, gps_file, kml_file);
+               cc_flightraw_free(raw);
+               if (has_summary && !summary_name) {
+                       fclose(summary_file); summary_file = NULL;
+               }
+               if (has_detail && !detail_name) {
+                       fclose(detail_file); detail_file = NULL;
+               }
+               if (this_plot_name && this_plot_name != plot_name) {
+                       free (this_plot_name); this_plot_name = NULL;
+               }
+               if (has_raw && !raw_name) {
+                       fclose(raw_file); raw_file = NULL;
+               }
+               if (has_gps && !gps_name) {
+                       fclose(gps_file); gps_file = NULL;
+               }
+               if (has_kml && !kml_name) {
+                       fclose(kml_file); kml_file = NULL;
+               }
+       }
+       return ret;
+}
index e79645f19a982653f633067474c412fadcad6ae5..30d0467d6255e6f287c0183da8a6ce56e89e5cea 100644 (file)
 ao-rawload \- flash a program to a AltOS device
 .SH SYNOPSIS
 .B "ao-rawload"
-[\-tty \fItty-device\fP]
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
 \fIfile.ihx\fP
 .SH DESCRIPTION
 .I ao-rawload
 loads the specified .ihx file, without modification, into the target
-device flash memory.
+device flash or ram (depending on the base address of the .ihx file).
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device the debugger uses to communicate with
+the target device. The special name 'BITBANG' directs ao-dbg to use
+the cp2103 connection, otherwise this should be a usb serial port
+connected to a suitable cc1111 debug node.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleMetrum:2
+.br
+TeleMetrum
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
+.TP
+\-r | --run
+After the file has been loaded, set the PC to the base address of the
+image and resume execution there.
+the .ihx file.
 .SH USAGE
 .I ao-rawload
 reads the specified .ihx file into memory. It then connects to the
 specified target device and writes the program to the target device
-flash memory.
+memory and, optionally, starts the program executing.
 .SH AUTHOR
 Keith Packard
index 1f1537b9f12d59eb49f6de87c7e96ed39e7e1e1f..a4746b195411ac8d73bb237774f12cea3020b808 100644 (file)
 #include <unistd.h>
 #include <getopt.h>
 #include "ccdbg.h"
+#include "cc.h"
 
 static const struct option options[] = {
        { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "run", .has_arg = 0, .val = 'r' },
        { 0, 0, 0, 0},
 };
 
 static void usage(char *program)
 {
-       fprintf(stderr, "usage: %s [--tty <tty-name>] file.ihx\n", program);
+       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [--run] file.ihx\n", program);
        exit(1);
 }
 
@@ -42,13 +45,21 @@ main (int argc, char **argv)
        char            *filename;
        FILE            *file;
        char            *tty = NULL;
+       char            *device = NULL;
        int             c;
+       int             run = 0;
 
-       while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "rT:D:", options, NULL)) != -1) {
                switch (c) {
                case 'T':
                        tty = optarg;
                        break;
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'r':
+                       run = 1;
+                       break;
                default:
                        usage(argv[0]);
                        break;
@@ -56,7 +67,7 @@ main (int argc, char **argv)
        }
        filename = argv[optind];
        if (filename == NULL) {
-               fprintf(stderr, "usage: %s <filename.ihx>\n", argv[0]);
+               usage(argv[0]);
                exit(1);
        }
        file = fopen(filename, "r");
@@ -75,6 +86,8 @@ main (int argc, char **argv)
        }
 
        ccdbg_hex_file_free(hex);
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "TIDongle");
        dbg = ccdbg_open(tty);
        if (!dbg)
                exit (1);
@@ -98,8 +111,10 @@ main (int argc, char **argv)
                ccdbg_close(dbg);
                exit(1);
        }
-       ccdbg_set_pc(dbg, image->address);
-       ccdbg_resume(dbg);
+       if (run) {
+               ccdbg_set_pc(dbg, image->address);
+               ccdbg_resume(dbg);
+       }
        ccdbg_close(dbg);
        exit (0);
 }
diff --git a/ao-tools/ao-send-telem/Makefile.am b/ao-tools/ao-send-telem/Makefile.am
new file mode 100644 (file)
index 0000000..bfddf13
--- /dev/null
@@ -0,0 +1,12 @@
+bin_PROGRAMS=ao-send-telem
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+AO_POSTFLIGHT_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_send_telem_DEPENDENCIES = $(AO_POSTFLIGHT_LIBS)
+
+ao_send_telem_LDADD=$(AO_POSTFLIGHT_LIBS) $(LIBUSB_LIBS)
+
+ao_send_telem_SOURCES = ao-send-telem.c
+
+man_MANS = ao-send-telem.1
diff --git a/ao-tools/ao-send-telem/ao-send-telem.1 b/ao-tools/ao-send-telem/ao-send-telem.1
new file mode 100644 (file)
index 0000000..fbdb2fe
--- /dev/null
@@ -0,0 +1,64 @@
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-SEND-TELEM 1 "ao-send-telem" ""
+.SH NAME
+ao-send-telem \- Re-transmit stored telemetry file
+.SH SYNOPSIS
+.B "ao-send-telem"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+[\-F \fIfrequency (kHz)\fP]
+[\--frequency \fIfrequency (kHz)\fP]
+[\-R]
+[\--realtime]
+<flight.telem>
+.SH OPTIONS
+.TP
+\-T tty-device | --tty tty-device
+This selects which tty device ao-dumplog uses to communicate with
+the target device.
+.TP
+\-D AltOS-device | --device AltOS-device
+Search for a connected device. This requires an argument of one of the
+following forms:
+.IP
+TeleDongle:2
+.br
+TeleDongle
+.br
+2
+.IP
+Leaving out the product name will cause the tool to select a suitable
+product, leaving out the serial number will cause the tool to match
+one of the available devices.
+.TP
+\-F kHz | --frequency kHz
+This selects which frequency to send the specified packets on.
+.TP
+\-R | --realtime
+This makes the program delay between packets in pad mode. Normally,
+pad mode packets are sent as quickly as possible.
+.SH DESCRIPTION
+.I ao-send-telem
+reads the specified flight telemetry log and re-transmits it via the
+specified ground station device
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-send-telem/ao-send-telem.c b/ao-tools/ao-send-telem/ao-send-telem.c
new file mode 100644 (file)
index 0000000..3db4454
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "cc.h"
+#include "cc-usb.h"
+
+static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "frequency", .has_arg = 1, .val = 'F' },
+       { .name = "realtime", .has_arg = 0, .val = 'R' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [--frequency <kHz>] [--realtime] file.telem ...\n", program);
+       exit(1);
+}
+
+#define bool(b)        ((b) ? "true" : "false")
+
+struct ao_telem_list {
+       struct ao_telem_list    *next;
+       union ao_telemetry_all  telem;
+};
+
+static struct ao_telem_list    *telem_list, **telem_last;
+
+static void
+trim_telem(uint16_t time)
+{
+       while (telem_list && (int16_t) (time - telem_list->telem.generic.tick) > 0) {
+               struct ao_telem_list    *next = telem_list->next;
+               free(telem_list);
+               telem_list = next;
+       }
+       if (!telem_list)
+               telem_last = &telem_list;
+}
+
+static void
+add_telem(union ao_telemetry_all *telem)
+{
+       struct ao_telem_list    *new = malloc (sizeof (struct ao_telem_list));
+       trim_telem((uint16_t) (telem->generic.tick - 20 * 100));
+       new->telem = *telem;
+       new->next = 0;
+       *telem_last = new;
+       telem_last = &new->next;
+}
+
+static enum ao_flight_state    cur_state = ao_flight_invalid;
+static enum ao_flight_state    last_state = ao_flight_invalid;
+
+static enum ao_flight_state
+packet_state(union ao_telemetry_all *telem)
+{
+       switch (telem->generic.type) {
+       case AO_TELEMETRY_SENSOR_TELEMETRUM:
+       case AO_TELEMETRY_SENSOR_TELEMINI:
+       case AO_TELEMETRY_SENSOR_TELENANO:
+               cur_state = telem->sensor.state;
+               break;
+       case AO_TELEMETRY_MEGA_DATA:
+               cur_state = telem->mega_data.state;
+               break;
+       }
+       return cur_state;
+}
+
+static const char *state_names[] = {
+       "startup",
+       "idle",
+       "pad",
+       "boost",
+       "fast",
+       "coast",
+       "drogue",
+       "main",
+       "landed",
+       "invalid"
+};
+
+static void
+send_telem(struct cc_usb *cc, union ao_telemetry_all *telem)
+{
+       int     rssi = (int8_t) telem->generic.rssi / 2 - 74;
+       int     i;
+       uint8_t *b;
+
+       packet_state(telem);
+       if (cur_state != last_state) {
+               if (0 <= cur_state && cur_state < sizeof(state_names) / sizeof (state_names[0]))
+                       printf ("%s\n", state_names[cur_state]);
+               last_state = cur_state;
+       }
+       cc_usb_printf(cc, "S 20\n");
+       b = (uint8_t *) telem;
+       for (i = 0; i < 0x20; i++)
+               cc_usb_printf(cc, "%02x", b[i]);
+       cc_usb_sync(cc);
+}      
+
+static void
+do_delay(uint16_t now, uint16_t then)
+{
+       int16_t delay = (int16_t) (now - then);
+
+       if (delay > 0 && delay < 1000)
+               usleep(delay * 10 * 1000);
+}
+
+static uint16_t
+send_queued(struct cc_usb *cc, int pause)
+{
+       struct ao_telem_list    *next;
+       uint16_t                tick = 0;
+       int                     started = 0;
+
+       while (telem_list) {
+               if (started && pause)
+                       do_delay(telem_list->telem.generic.tick, tick);
+               tick = telem_list->telem.generic.tick;
+               started = 1;
+               send_telem(cc, &telem_list->telem);
+
+               next = telem_list->next;
+               free(telem_list);
+               telem_list = next;
+       }
+       return tick;
+}
+
+int
+main (int argc, char **argv)
+{
+       struct cc_usb   *cc;
+       char            *tty = NULL;
+       char            *device = NULL;
+       char            line[80];
+       int             c, i, ret = 0;
+       int             freq = 434550;
+       char            *s;
+       FILE            *file;
+       int             serial;
+       uint16_t        last_tick;
+       int             started;
+       int             realtime = 0;
+      
+
+       while ((c = getopt_long(argc, argv, "RT:D:F:", options, NULL)) != -1) {
+               switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'F':
+                       freq = atoi(optarg);
+                       break;
+               case 'R':
+                       realtime = 1;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "TeleDongle");
+       if (!tty)
+               tty = getenv("ALTOS_TTY");
+       if (!tty)
+               tty="/dev/ttyACM0";
+       cc = cc_usb_open(tty);
+       if (!cc)
+               exit (1);
+
+       cc_usb_printf(cc, "m 0\n");
+       cc_usb_printf(cc, "c F %d\n", freq);
+       for (i = optind; i < argc; i++) {
+               file = fopen(argv[i], "r");
+               if (!file) {
+                       perror(argv[i]);
+                       ret++;
+                       continue;
+               }
+               started = 0;
+               last_tick = 0;
+               while (fgets(line, sizeof (line), file)) {
+                       union ao_telemetry_all telem;
+
+                       if (cc_telemetry_parse(line, &telem)) {
+                               /*
+                                * Skip packets with CRC errors.
+                                */
+                               if ((telem.generic.status & (1 << 7)) == 0)
+                                       continue;
+
+                               if (started) {
+                                       do_delay(telem.generic.tick, last_tick);
+                                       last_tick = telem.generic.tick;
+                                       send_telem(cc, &telem);
+                               } else {
+                                       enum ao_flight_state state = packet_state(&telem);
+                                       add_telem(&telem);
+                                       if (ao_flight_pad < state && state < ao_flight_landed) {
+                                               printf ("started\n");
+                                               started = 1;
+                                               last_tick = send_queued(cc, realtime);
+                                       }
+                               }
+                       }
+               }
+               fclose (file);
+
+       }
+       return ret;
+}
diff --git a/ao-tools/ao-stmload/.gitignore b/ao-tools/ao-stmload/.gitignore
new file mode 100644 (file)
index 0000000..dedb009
--- /dev/null
@@ -0,0 +1 @@
+ao-stmload
diff --git a/ao-tools/ao-stmload/Makefile.am b/ao-tools/ao-stmload/Makefile.am
new file mode 100644 (file)
index 0000000..5aea7db
--- /dev/null
@@ -0,0 +1,15 @@
+if LIBSTLINK
+
+bin_PROGRAMS=ao-stmload
+
+LIBSTLINKDIR=/local/src/stlink
+
+AM_CFLAGS=$(LIBSTLINK_CFLAGS) $(LIBUSB_CFLAGS) -I../lib
+
+ao_stmload_LDADD=$(LIBSTLINK_LIBS) $(LIBUSB_LIBS) -lelf
+
+ao_stmload_SOURCES=ao-stmload.c
+
+man_MANS = ao-stmload.1
+
+endif
diff --git a/ao-tools/ao-stmload/ao-stmload.1 b/ao-tools/ao-stmload/ao-stmload.1
new file mode 100644 (file)
index 0000000..38e9c17
--- /dev/null
@@ -0,0 +1,61 @@
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-LOAD 1 "ao-stmload" ""
+.SH NAME
+ao-stmload \- flash a program to an STM32-based AltOS device
+.SH SYNOPSIS
+.B "ao-stmload"
+[\-D \fI/dev/sgX\fP]
+[\--device \fI/dev/sgX\fP]
+[\--cal \fIradio-calibration\fP]
+[\--serial \fserial-number\fP]
+\fIfile.elf\fP
+.SH DESCRIPTION
+.I ao-stmload
+loads the specified .elf file into the target device flash memory,
+using either existing serial number and radio calibration values or
+taking either of those from the command line.
+.SH OPTIONS
+.TP
+\-D /dev/sgX | --device /dev/sgX
+This targets an STlinkV1 connection rather than STlinkV2
+.TP
+\-s serial-number | --serial serial-number
+This programs the device serial number into the image. If no serial
+number is specified, then the existing serial number, if any, will be
+read from the device.
+.TP
+\-c radio-calibration | --cal radio-calibration This programs the
+radio calibration value into the image for hardware which doesn't have
+any eeprom storage for this value. If no calibration value is
+specified, an existing calibration value will be used. The value here
+can be computed given the current radio calibration value, the
+measured frequency and the desired frequency:
+.IP
+       cal' = cal * (desired/measured)
+.IP
+The default calibration value is 7119667.
+.SH USAGE
+.I ao-stmload
+reads the specified .elf file into memory, edits the image to
+customize it using the specified serial number and radio calibration
+values. It then connects to the debug dongle and writes the program to
+the target device flash memory.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-stmload/ao-stmload.c b/ao-tools/ao-stmload/ao-stmload.c
new file mode 100644 (file)
index 0000000..a471dcc
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <err.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <string.h>
+#include "stlink-common.h"
+
+#define AO_USB_DESC_STRING             3
+
+struct sym {
+       unsigned        addr;
+       char            *name;
+       int             required;
+} ao_symbols[] = {
+
+       { 0,    "ao_romconfig_version", 1 },
+#define AO_ROMCONFIG_VERSION   (ao_symbols[0].addr)
+
+       { 0,    "ao_romconfig_check",   1 },
+#define AO_ROMCONFIG_CHECK     (ao_symbols[1].addr)
+
+       { 0,    "ao_serial_number", 1 },
+#define AO_SERIAL_NUMBER       (ao_symbols[2].addr)
+
+       { 0,    "ao_usb_descriptors", 0 },
+#define AO_USB_DESCRIPTORS     (ao_symbols[3].addr)
+
+       { 0,    "ao_radio_cal", 0 },
+#define AO_RADIO_CAL           (ao_symbols[4].addr)
+};
+
+#define NUM_SYMBOLS            5
+#define NUM_REQUIRED_SYMBOLS   3
+
+/*
+ * Look through the Elf file for the AltOS symbols
+ * that can be adjusted before the image is written
+ * to the device
+ */
+static int
+find_symbols (Elf *e)
+{
+       Elf_Scn         *scn;
+       Elf_Data        *symbol_data = NULL;
+       GElf_Shdr       shdr;
+       GElf_Sym        sym;
+       int             i, symbol_count, s;
+       int             required = 0;
+       char            *symbol_name;
+
+       /*
+        * Find the symbols
+        */
+
+       scn = NULL;
+       while ((scn = elf_nextscn(e, scn)) != NULL) {
+               if (gelf_getshdr(scn, &shdr) != &shdr)
+                       return 0;
+
+               if (shdr.sh_type == SHT_SYMTAB) {
+                       symbol_data = elf_getdata(scn, NULL);
+                       symbol_count = shdr.sh_size / shdr.sh_entsize;
+                       break;
+               }
+       }
+
+       if (!symbol_data)
+               return 0;
+
+       for (i = 0; i < symbol_count; i++) {
+               gelf_getsym(symbol_data, i, &sym);
+
+               symbol_name = elf_strptr(e, shdr.sh_link, sym.st_name);
+
+               for (s = 0; s < NUM_SYMBOLS; s++)
+                       if (!strcmp (ao_symbols[s].name, symbol_name)) {
+                               int     t;
+                               ao_symbols[s].addr = sym.st_value;
+                               if (ao_symbols[s].required)
+                                       ++required;
+                       }
+       }
+
+       return required >= NUM_REQUIRED_SYMBOLS;
+}
+
+struct load {
+       uint32_t        addr;
+       uint32_t        len;
+       uint8_t         buf[0];
+};
+
+uint32_t round4(uint32_t a) {
+       return (a + 3) & ~3;
+}
+
+struct load *
+new_load (uint32_t addr, uint32_t len)
+{
+       struct load *new;
+
+       len = round4(len);
+       new = calloc (1, sizeof (struct load) + len);
+       if (!new)
+               abort();
+
+       new->addr = addr;
+       new->len = len;
+       return new;
+}
+
+void
+load_paste(struct load *into, struct load *from)
+{
+       if (from->addr < into->addr || into->addr + into->len < from->addr + from->len)
+               abort();
+
+       memcpy(into->buf + from->addr - into->addr, from->buf, from->len);
+}
+
+/*
+ * Make a new load structure large enough to hold the old one and
+ * the new data
+ */
+struct load *
+expand_load(struct load *from, uint32_t addr, uint32_t len)
+{
+       struct load     *new;
+
+       if (from) {
+               uint32_t        from_last = from->addr + from->len;
+               uint32_t        last = addr + len;
+
+               if (addr > from->addr)
+                       addr = from->addr;
+               if (last < from_last)
+                       last = from_last;
+
+               len = last - addr;
+
+               if (addr == from->addr && len == from->len)
+                       return from;
+       }
+       new = new_load(addr, len);
+       if (from) {
+               load_paste(new, from);
+               free (from);
+       }
+       return new;
+}
+
+/*
+ * Create a new load structure with data from the existing one
+ * and the new data
+ */
+struct load *
+load_write(struct load *from, uint32_t addr, uint32_t len, void *data)
+{
+       struct load     *new;
+
+       new = expand_load(from, addr, len);
+       memcpy(new->buf + addr - new->addr, data, len);
+       return new;
+}
+
+/*
+ * Construct a large in-memory block for all
+ * of the loaded sections of the program
+ */
+static struct load *
+get_load(Elf *e)
+{
+       Elf_Scn         *scn;
+       size_t          shstrndx;
+       GElf_Shdr       shdr;
+       Elf_Data        *data;
+       uint8_t         *buf;
+       char            *got_name;
+       size_t          nphdr;
+       int             p;
+       GElf_Phdr       phdr;
+       struct load     *load = NULL;
+       
+       if (elf_getshdrstrndx(e, &shstrndx) < 0)
+               return 0;
+
+       if (elf_getphdrnum(e, &nphdr) < 0)
+               return 0;
+
+       /*
+        * As far as I can tell, all of the phdr sections should
+        * be flashed to memory
+        */
+       for (p = 0; p < nphdr; p++) {
+
+               /* Find this phdr */
+               gelf_getphdr(e, p, &phdr);
+
+               /* Get the associated file section */
+               scn = gelf_offscn(e, phdr.p_offset);
+
+               if (gelf_getshdr(scn, &shdr) != &shdr)
+                       abort();
+
+               data = elf_getdata(scn, NULL);
+
+               /* Write the section data into the memory block */
+               load = load_write(load, phdr.p_paddr, phdr.p_filesz, data->d_buf);
+       }
+       return load;
+}
+
+/*
+ * Edit the to-be-written memory block
+ */
+static int
+rewrite(struct load *load, unsigned addr, uint8_t *data, int len)
+{
+       int             i;
+
+       if (addr < load->addr || load->addr + load->len < addr + len)
+               return 0;
+
+       printf("rewrite %04x:", addr);
+       for (i = 0; i < len; i++)
+               printf (" %02x", load->buf[addr - load->addr + i]);
+       printf(" ->");
+       for (i = 0; i < len; i++)
+               printf (" %02x", data[i]);
+       printf("\n");
+       memcpy(load->buf + addr - load->addr, data, len);
+}
+
+/*
+ * Open the specified ELF file and
+ * check for the symbols we need
+ */
+
+Elf *
+ao_open_elf(char *name)
+{
+       int             fd;
+       Elf             *e;
+       Elf_Scn         *scn;
+       Elf_Data        *symbol_data = NULL;
+       GElf_Shdr       shdr;
+       GElf_Sym        sym;
+       size_t          n, shstrndx, sz;
+       int             i, symbol_count, s;
+       int             required = 0;
+
+       if (elf_version(EV_CURRENT) == EV_NONE)
+               return NULL;
+
+       fd = open(name, O_RDONLY, 0);
+
+       if (fd < 0)
+               return NULL;
+
+       e = elf_begin(fd, ELF_C_READ, NULL);
+
+       if (!e)
+               return NULL;
+
+       if (elf_kind(e) != ELF_K_ELF)
+               return NULL;
+
+       if (elf_getshdrstrndx(e, &shstrndx) != 0)
+               return NULL;
+
+       if (!find_symbols(e)) {
+               fprintf (stderr, "Cannot find required symbols\n");
+               return NULL;
+       }
+
+       return e;
+}
+
+/*
+ * Read a 32-bit value from the target device with arbitrary
+ * alignment
+ */
+static uint32_t
+get_uint32(stlink_t *sl, uint32_t addr)
+{
+       const           uint8_t *data = sl->q_buf;
+       uint32_t        actual_addr;
+       int             off;
+       uint32_t        result;
+
+       sl->q_len = 0;
+
+       printf ("read 0x%x\n", addr);
+
+       actual_addr = addr & ~3;
+       
+       stlink_read_mem32(sl, actual_addr, 8);
+
+       if (sl->q_len != 8)
+               abort();
+
+       off = addr & 3;
+       result = data[off] | (data[off + 1] << 8) | (data[off+2] << 16) | (data[off+3] << 24);
+       printf ("read 0x%08x = 0x%08x\n", addr, result);
+       return result;
+}
+
+/*
+ * Read a 16-bit value from the target device with arbitrary
+ * alignment
+ */
+static uint16_t
+get_uint16(stlink_t *sl, uint32_t addr)
+{
+       const           uint8_t *data = sl->q_buf;
+       uint32_t        actual_addr;
+       int             off;
+       uint16_t        result;
+
+       sl->q_len = 0;
+
+
+       actual_addr = addr & ~3;
+       
+       stlink_read_mem32(sl, actual_addr, 8);
+
+       if (sl->q_len != 8)
+               abort();
+
+       off = addr & 3;
+       result = data[off] | (data[off + 1] << 8);
+       printf ("read 0x%08x = 0x%04x\n", addr, result);
+       return result;
+}
+
+/*
+ * Check to see if the target device has been
+ * flashed with a similar firmware image before
+ *
+ * This is done by looking for the same romconfig version,
+ * which should be at the same location as the linker script
+ * places this at 0x100 from the start of the rom section
+ */
+static int
+check_flashed(stlink_t *sl)
+{
+       uint16_t        romconfig_version = get_uint16(sl, AO_ROMCONFIG_VERSION);
+       uint16_t        romconfig_check = get_uint16(sl, AO_ROMCONFIG_CHECK);
+
+       if (romconfig_version != (uint16_t) ~romconfig_check) {
+               fprintf (stderr, "Device has not been flashed before\n");
+               return 0;
+       }
+       return 1;
+}
+
+static const struct option options[] = {
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "cal", .has_arg = 1, .val = 'c' },
+       { .name = "serial", .has_arg = 1, .val = 's' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--cal=<radio-cal>] [--serial=<serial>] file.elf\n", program);
+       exit(1);
+}
+
+void
+done(stlink_t *sl, int code)
+{
+       if (sl) {
+               stlink_reset(sl);
+               stlink_run(sl);
+               stlink_exit_debug_mode(sl);
+               stlink_close(sl);
+       }
+       exit (code);
+}
+
+int
+main (int argc, char **argv)
+{
+       char                    *device = NULL;
+       char                    *filename;
+       Elf                     *e;
+       char                    *serial_end;
+       unsigned int            serial = 0;
+       char                    *serial_ucs2;
+       int                     serial_ucs2_len;
+       char                    serial_int[2];
+       unsigned int            s;
+       int                     i;
+       int                     string_num;
+       uint32_t                cal = 0;
+       char                    cal_int[4];
+       char                    *cal_end;
+       int                     c;
+       stlink_t                *sl;
+       int                     was_flashed = 0;
+       struct load             *load;
+
+       while ((c = getopt_long(argc, argv, "D:c:s:", options, NULL)) != -1) {
+               switch (c) {
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'c':
+                       cal = strtoul(optarg, &cal_end, 10);
+                       if (cal_end == optarg || *cal_end != '\0')
+                               usage(argv[0]);
+                       break;
+               case 's':
+                       serial = strtoul(optarg, &serial_end, 10);
+                       if (serial_end == optarg || *serial_end != '\0')
+                               usage(argv[0]);
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+
+       filename = argv[optind];
+       if (filename == NULL)
+               usage(argv[0]);
+
+       /*
+        * Open the source file and load the symbols and
+        * flash data
+        */
+       
+       e = ao_open_elf(filename);
+       if (!e) {
+               fprintf(stderr, "Cannot open file \"%s\"\n", filename);
+               exit(1);
+       }
+
+       if (!find_symbols(e)) {
+               fprintf(stderr, "Cannot find symbols in \"%s\"\n", filename);
+               exit(1);
+       }
+
+       if (!(load = get_load(e))) {
+               fprintf(stderr, "Cannot find program data in \"%s\"\n", filename);
+               exit(1);
+       }
+               
+       /* Connect to the programming dongle
+        */
+       
+       if (device) {
+               sl = stlink_v1_open(50);
+       } else {
+               sl = stlink_open_usb(50);
+               
+       }
+       if (!sl) {
+               fprintf (stderr, "No STLink devices present\n");
+               done (sl, 1);
+       }
+
+       sl->verbose = 50;
+
+       /* Verify that the loaded image fits entirely within device flash
+        */
+       if (load->addr < sl->flash_base ||
+           sl->flash_base + sl->flash_size < load->addr + load->len) {
+               fprintf (stderr, "\%s\": Invalid memory range 0x%08x - 0x%08x\n", filename,
+                        load->addr, load->addr + load->len);
+               done(sl, 1);
+       }
+
+       /* Enter debugging mode
+        */
+       if (stlink_current_mode(sl) == STLINK_DEV_DFU_MODE)
+               stlink_exit_dfu_mode(sl);
+
+       if (stlink_current_mode(sl) != STLINK_DEV_DEBUG_MODE)
+               stlink_enter_swd_mode(sl);
+
+       /* Go fetch existing config values
+        * if available
+        */
+       was_flashed = check_flashed(sl);
+
+       if (!serial) {
+               if (!was_flashed) {
+                       fprintf (stderr, "Must provide serial number\n");
+                       done(sl, 1);
+               }
+               serial = get_uint16(sl, AO_SERIAL_NUMBER);
+               if (!serial || serial == 0xffff) {
+                       fprintf (stderr, "Invalid existing serial %d\n", serial);
+                       done(sl, 1);
+               }
+       }
+
+       if (!cal && AO_RADIO_CAL && was_flashed) {
+               cal = get_uint32(sl, AO_RADIO_CAL);
+               if (!cal || cal == 0xffffffff) {
+                       fprintf (stderr, "Invalid existing rf cal %d\n", cal);
+                       done(sl, 1);
+               }
+       }
+
+       /* Write the config values into the flash image
+        */
+
+       serial_int[0] = serial & 0xff;
+       serial_int[1] = (serial >> 8) & 0xff;
+
+       if (!rewrite(load, AO_SERIAL_NUMBER, serial_int, sizeof (serial_int))) {
+               fprintf(stderr, "Cannot rewrite serial integer at %08x\n",
+                       AO_SERIAL_NUMBER);
+               done(sl, 1);
+       }
+
+       if (AO_USB_DESCRIPTORS) {
+               unsigned        usb_descriptors;
+               usb_descriptors = AO_USB_DESCRIPTORS - load->addr;
+               string_num = 0;
+
+               while (load->buf[usb_descriptors] != 0 && usb_descriptors < load->len) {
+                       if (load->buf[usb_descriptors+1] == AO_USB_DESC_STRING) {
+                               ++string_num;
+                               if (string_num == 4)
+                                       break;
+                       }
+                       usb_descriptors += load->buf[usb_descriptors];
+               }
+               if (usb_descriptors >= load->len || load->buf[usb_descriptors] == 0 ) {
+                       fprintf(stderr, "Cannot rewrite serial string at %08x\n", AO_USB_DESCRIPTORS);
+                       done(sl, 1);
+               }
+
+               serial_ucs2_len = load->buf[usb_descriptors] - 2;
+               serial_ucs2 = malloc(serial_ucs2_len);
+               if (!serial_ucs2) {
+                       fprintf(stderr, "Malloc(%d) failed\n", serial_ucs2_len);
+                       done(sl, 1);
+               }
+               s = serial;
+               for (i = serial_ucs2_len / 2; i; i--) {
+                       serial_ucs2[i * 2 - 1] = 0;
+                       serial_ucs2[i * 2 - 2] = (s % 10) + '0';
+                       s /= 10;
+               }
+               if (!rewrite(load, usb_descriptors + 2 + load->addr, serial_ucs2, serial_ucs2_len)) {
+                       fprintf (stderr, "Cannot rewrite USB descriptor at %08x\n", AO_USB_DESCRIPTORS);
+                       done(sl, 1);
+               }
+       }
+
+       if (cal && AO_RADIO_CAL) {
+               cal_int[0] = cal & 0xff;
+               cal_int[1] = (cal >> 8) & 0xff;
+               cal_int[2] = (cal >> 16) & 0xff;
+               cal_int[3] = (cal >> 24) & 0xff;
+
+               if (!rewrite(load, AO_RADIO_CAL, cal_int, sizeof (cal_int))) {
+                       fprintf(stderr, "Cannot rewrite radio calibration at %08x\n", AO_RADIO_CAL);
+                       exit(1);
+               }
+       }
+
+       /* And flash the resulting image to the device
+        */
+       if (stlink_write_flash(sl, load->addr, load->buf, load->len) < 0) {
+               fprintf (stderr, "\"%s\": Write failed\n", filename);
+               done(sl, 1);
+       }
+
+       done(sl, 0);
+}
diff --git a/ao-tools/ao-telem/.gitignore b/ao-tools/ao-telem/.gitignore
new file mode 100644 (file)
index 0000000..5ab6f64
--- /dev/null
@@ -0,0 +1 @@
+ao-telem
diff --git a/ao-tools/ao-telem/Makefile.am b/ao-tools/ao-telem/Makefile.am
new file mode 100644 (file)
index 0000000..3436443
--- /dev/null
@@ -0,0 +1,12 @@
+bin_PROGRAMS=ao-telem
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+AO_POSTFLIGHT_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_telem_DEPENDENCIES = $(AO_POSTFLIGHT_LIBS)
+
+ao_telem_LDADD=$(AO_POSTFLIGHT_LIBS) $(LIBUSB_LIBS)
+
+ao_telem_SOURCES = ao-telem.c
+
+man_MANS = ao-telem.1
diff --git a/ao-tools/ao-telem/ao-telem.1 b/ao-tools/ao-telem/ao-telem.1
new file mode 100644 (file)
index 0000000..8e6699d
--- /dev/null
@@ -0,0 +1,61 @@
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-TELEM 1 "ao-telem" ""
+.SH NAME
+ao-telem \- Analyse a flight log (either telemetry or eeprom)
+.SH SYNOPSIS
+.B "ao-telem"
+[\-s <summary-file>]
+[\--summary=<summary-file>]
+[\-d <detail-file>]
+[\--detail=<detail-file>]
+[\-r <raw-file>]
+[\--raw=<raw-file>]
+[\-p <plot-file>]
+[\--plot=<plot-file>]
+[\-g <gps-file]
+[\--gps=<gps-file]
+[\-k <kml-file]
+[\--kml=<kml-file]
+{flight.eeprom|flight.telem}
+.SH DESCRIPTION
+.I ao-telem
+reads the specified flight log and produces several different kinds of
+output.
+.IP Summary
+By default, summary information is shown on stdout. With the --summary
+option, it can be redirected to a file.
+.IP Detail
+When requested with the --detail option, a filtered version of the
+flight position, speed and acceleration are written to the specified
+file.
+.IP Raw
+The --raw option writes the unfiltered, but converted acceleration
+and height data to the specified file.
+.IP Plot
+The --plot option writes plots of height, speed and acceleration to
+the specified file in .svg format
+.IP GPS
+The --gps option writes the recorded GPS data to the specified file in
+three columns.
+.IP KML
+The --kml option writes the recorded GPS data to the specified file in
+Keyhole Markup Language format, which can be displayed in Googleearth.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-telem/ao-telem.c b/ao-tools/ao-telem/ao-telem.c
new file mode 100644 (file)
index 0000000..e7fc8e2
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include "cc.h"
+
+static const struct option options[] = {
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s\n"
+               "\t{flight-log} ...\n", program);
+       exit(1);
+}
+
+#define bool(b)        ((b) ? "true" : "false")
+
+int
+main (int argc, char **argv)
+{
+       char    line[80];
+       int c, i, ret;
+       char *s;
+       FILE *file;
+       int serial;
+       while ((c = getopt_long(argc, argv, "", options, NULL)) != -1) {
+               switch (c) {
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+       for (i = optind; i < argc; i++) {
+               file = fopen(argv[i], "r");
+               if (!file) {
+                       perror(argv[i]);
+                       ret++;
+                       continue;
+               }
+               s = strstr(argv[i], "-serial-");
+               if (s)
+                       serial = atoi(s + 8);
+               else
+                       serial = 0;
+               while (fgets(line, sizeof (line), file)) {
+                       union ao_telemetry_all telem;
+                       char call[AO_MAX_CALLSIGN+1];
+                       char version[AO_MAX_VERSION+1];
+
+                       if (cc_telemetry_parse(line, &telem)) {
+                               int rssi = (int8_t) telem.generic.rssi / 2 - 74;
+
+                               printf ("serial %5d rssi %d status %02x tick %5d type %3d ",
+                                       telem.generic.serial, rssi, telem.generic.status,
+                                       telem.generic.tick, telem.generic.type);
+                               if ((telem.generic.status & (1 << 7)) == 0) {
+                                       printf ("CRC error\n");
+                                       continue;
+                               }
+                               switch (telem.generic.type) {
+                               case AO_TELEMETRY_SENSOR_TELEMETRUM:
+                               case AO_TELEMETRY_SENSOR_TELEMINI:
+                               case AO_TELEMETRY_SENSOR_TELENANO:
+                                       printf ("state %1d accel %5d pres %5d ",
+                                               telem.sensor.state, telem.sensor.accel, telem.sensor.pres);
+                                       printf ("accel %6.2f speed %6.2f height %5d ",
+                                               telem.sensor.acceleration / 16.0,
+                                               telem.sensor.speed / 16.0,
+                                               telem.sensor.height);
+                                       printf ("ground_pres %5d ground_accel %5d accel_plus %5d accel_minus %5d\n",
+                                               telem.sensor.ground_pres,
+                                               telem.sensor.ground_accel,
+                                               telem.sensor.accel_plus_g,
+                                               telem.sensor.accel_minus_g);
+                                       break;
+                               case AO_TELEMETRY_CONFIGURATION:
+                                       memcpy(call, telem.configuration.callsign, AO_MAX_CALLSIGN);
+                                       memcpy(version, telem.configuration.version, AO_MAX_VERSION);
+                                       call[AO_MAX_CALLSIGN] = '\0';
+                                       version[AO_MAX_CALLSIGN] = '\0';
+                                       printf ("device %3d flight %5d config %3d.%03d delay %2d main %4d",
+                                               telem.configuration.device,
+                                               telem.configuration.flight,
+                                               telem.configuration.config_major,
+                                               telem.configuration.config_minor,
+                                               telem.configuration.apogee_delay,
+                                               telem.configuration.main_deploy,
+                                               telem.configuration.flight_log_max);
+                                       printf (" call %8s version %8s\n", call, version);
+                                       break;
+                               case AO_TELEMETRY_LOCATION:
+                                       printf ("sats %d flags %s%s%s%s",
+                                               telem.location.flags & 0xf,
+                                               (telem.location.flags & (1 << 4)) ? "valid" : "invalid",
+                                               (telem.location.flags & (1 << 5)) ? ",running" : "",
+                                               (telem.location.flags & (1 << 6)) ? ",date" : "",
+                                               (telem.location.flags & (1 << 7)) ? ",course" : "");
+                                       printf (" alt %5d lat %12.7f lon %12.7f",
+                                               telem.location.altitude,
+                                               telem.location.latitude / 1e7,
+                                               telem.location.longitude / 1e7);
+                                       if ((telem.location.flags & (1 << 6)) != 0) {
+                                               printf (" year %2d month %2d day %2d",
+                                                       telem.location.year,
+                                                       telem.location.month,
+                                                       telem.location.day);
+                                               printf (" hour %2d minute %2d second %2d",
+                                                       telem.location.hour,
+                                                       telem.location.minute,
+                                                       telem.location.second);
+                                       }
+                                       printf (" pdop %3.1f hdop %3.1f vdop %3.1f mode %d",
+                                               telem.location.pdop / 5.0,
+                                               telem.location.hdop / 5.0,
+                                               telem.location.vdop / 5.0,
+                                               telem.location.mode);
+                                       if ((telem.location.flags & (1 << 7)) != 0)
+                                               printf (" ground_speed %6.2f climb_rate %6.2f course %d",
+                                                       telem.location.ground_speed / 100.0,
+                                                       telem.location.climb_rate / 100.0,
+                                                       telem.location.course * 2);
+                                       printf ("\n");
+                                       break;
+                               case AO_TELEMETRY_SATELLITE:
+                                       printf ("sats %d", telem.satellite.channels);
+                                       for (c = 0; c < 12 && c < telem.satellite.channels; c++) {
+                                               printf (" sat %d svid %d c_n_1 %d",
+                                                       c,
+                                                       telem.satellite.sats[c].svid,
+                                                       telem.satellite.sats[c].c_n_1);
+                                       }
+                                       printf ("\n");
+                                       break;
+                               case AO_TELEMETRY_MEGA_SENSOR:
+                                       printf ("accel %5d pres %9d temp %5d accel_x %5d accel_y %5d accel_z %5d gyro_x %5d gyro_y %5d gyro_z %5d mag_x %5d mag_y %5d mag_z %5d\n",
+                                               telem.mega_sensor.accel,
+                                               telem.mega_sensor.pres,
+                                               telem.mega_sensor.temp,
+                                               telem.mega_sensor.accel_x,
+                                               telem.mega_sensor.accel_y,
+                                               telem.mega_sensor.accel_z,
+                                               telem.mega_sensor.gyro_x,
+                                               telem.mega_sensor.gyro_y,
+                                               telem.mega_sensor.gyro_z,
+                                               telem.mega_sensor.mag_x,
+                                               telem.mega_sensor.mag_y,
+                                               telem.mega_sensor.mag_z);
+                                       break;
+                               case AO_TELEMETRY_MEGA_DATA:
+                                       printf ("state %1d v_batt %5d v_pyro %5d ",
+                                               telem.mega_data.state,
+                                               telem.mega_data.v_batt,
+                                               telem.mega_data.v_pyro);
+                                       for (c = 0; c < 6; c++)
+                                               printf ("s%1d %5d ", c,
+                                                       telem.mega_data.sense[c] |
+                                                       (telem.mega_data.sense[c] << 8));
+                                       
+                                       printf ("ground_pres %5d ground_accel %5d accel_plus %5d accel_minus %5d ",
+                                               telem.mega_data.ground_pres,
+                                               telem.mega_data.ground_accel,
+                                               telem.mega_data.accel_plus_g,
+                                               telem.mega_data.accel_minus_g);
+
+                                       printf ("accel %6.2f speed %6.2f height %5d\n",
+                                               telem.mega_data.acceleration / 16.0,
+                                               telem.mega_data.speed / 16.0,
+                                               telem.mega_data.height);
+
+                                       break;
+                               default:
+                                       printf("\n");
+                               }
+                       }
+               }
+               fclose (file);
+
+       }
+       return ret;
+}
diff --git a/ao-tools/ao-view/.gitignore b/ao-tools/ao-view/.gitignore
new file mode 100644 (file)
index 0000000..24fbc59
--- /dev/null
@@ -0,0 +1,4 @@
+*.o
+aoview
+aoview_glade.h
+aoview_flite
diff --git a/ao-tools/ao-view/Makefile.am b/ao-tools/ao-view/Makefile.am
new file mode 100644 (file)
index 0000000..7a28841
--- /dev/null
@@ -0,0 +1,38 @@
+VERSION=$(shell git describe)
+
+AO_VIEW_CFLAGS=-I$(top_srcdir)/ao-tools/lib
+AO_VIEW_LIBS=$(top_builddir)/ao-tools/lib/libao-tools.a
+AM_CFLAGS=$(AO_VIEW_CFLAGS) $(GNOME_CFLAGS) $(ALSA_CFLAGS) -I$(top_srcdir)/src -DAOVIEW_VERSION=\"$(VERSION)\" @FLITE_INCS@
+
+bin_PROGRAMS=ao-view
+
+ao_view_DEPENDENCIES=$(AO_VIEW_LIBS)
+ao_view_LDADD=$(GNOME_LIBS) $(FLITE_LIBS) $(ALSA_LIBS) $(AO_VIEW_LIBS) $(LIBUSB_LIBS)
+
+ao_view_SOURCES = \
+       aoview_main.c \
+       aoview_dev_dialog.c \
+       aoview_serial.c \
+       aoview_monitor.c \
+       aoview_state.c \
+       aoview_convert.c \
+       aoview_log.c \
+       aoview_table.c \
+       aoview_util.c \
+       aoview_file.c \
+       aoview_eeprom.c \
+       aoview_voice.c \
+       aoview_replay.c \
+       aoview_label.c \
+       aoview_flite.c \
+       aoview_channel.c \
+       aoview.h
+
+BUILT_SOURCES = aoview_glade.h
+
+CLEANFILES = aoview_glade.h
+
+man_MANS=ao-view.1
+
+aoview_glade.h: aoview.glade
+       sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/"/' $< > $@
diff --git a/ao-tools/ao-view/ao-view.1 b/ao-tools/ao-view/ao-view.1
new file mode 100644 (file)
index 0000000..99834c4
--- /dev/null
@@ -0,0 +1,50 @@
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-VIEW 1 "ao-view" ""
+.SH NAME
+ao-view \- Rocket flight monitor
+.SH SYNOPSIS
+.B "ao-view"
+[\--tty \fItty-device\fP]
+.SH DESCRIPTION
+.I ao-view
+connects to a TeleDongle or TeleMetrum device through a USB serial device.
+It provides a user interface to monitor, record and review rocket flight data.
+.SH OPTIONS
+The usual Gtk+ command line options can be used, along with
+.IP "\--tty"
+This selects a target device to connect at startup time to.
+The target device may also be selected through the user interface.
+.SH USAGE
+When connected to a TeleDongle device, ao-view turns on the radio
+receiver and listens for telemetry packets. It displays the received
+telemetry data, and reports flight status via voice synthesis. All
+received telemetry information is recorded to a file.
+.P
+When connected to a TeleMetrum device, ao-view downloads the eeprom
+data and stores it in a file.
+.SH FILES
+All data log files are recorded into a user-specified directory
+(default ~/AltOS). Files are named using the current date, the serial
+number of the reporting device, the flight number recorded in the data
+and either '.telem' for telemetry data or '.eeprom' for eeprom data.
+.SH "SEE ALSO"
+ao-load(1), ao-eeprom(1)
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-view/aoview.glade b/ao-tools/ao-view/aoview.glade
new file mode 100644 (file)
index 0000000..c302ad0
--- /dev/null
@@ -0,0 +1,845 @@
+<?xml version="1.0"?>
+<glade-interface>
+  <!-- interface-requires gtk+ 2.16 -->
+  <!-- interface-naming-policy project-wide -->
+  <widget class="GtkWindow" id="aoview">
+    <property name="width_request">900</property>
+    <property name="height_request">700</property>
+    <property name="visible">True</property>
+    <property name="title" translatable="yes">AltOS View</property>
+    <child>
+      <widget class="GtkVBox" id="vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <widget class="GtkMenuBar" id="menubar1">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem1">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_File</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu1">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem1">
+                        <property name="label">gtk-new</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem2">
+                        <property name="label">gtk-open</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem3">
+                        <property name="label">gtk-save</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem4">
+                        <property name="label">gtk-save-as</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
+                        <property name="visible">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem5">
+                        <property name="label">gtk-quit</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="activate" handler="gtk_main_quit"/>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem2">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Edit</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu2">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem6">
+                        <property name="label">gtk-cut</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem7">
+                        <property name="label">gtk-copy</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem8">
+                        <property name="label">gtk-paste</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem9">
+                        <property name="label">gtk-delete</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem3">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Device</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu4">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_connect">
+                        <property name="label" translatable="yes">_Connect to device</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate_item" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
+                        <signal name="activate" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image1">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-connect</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_disconnect">
+                        <property name="label" translatable="yes">_Disconnect</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image2">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-disconnect</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkSeparatorMenuItem" id="seperator">
+                        <property name="visible">True</property>
+                        <property name="sensitive">False</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_savelog">
+                        <property name="label" translatable="yes">_Save EEPROM data</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate_item" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
+                        <signal name="activate" handler="gtk_widget_show" object="device_connect_dialog"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image5">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-save</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="ao_replay">
+                        <property name="label" translatable="yes">_Replay</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate_item" handler="gtk_widget_show" object="ao_replay_dialog" after="yes"/>
+                        <signal name="activate" handler="gtk_widget_show" object="ao_replay_dialog"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image6">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-media-play</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem5">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Log</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu5">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="log_new">
+                        <property name="label" translatable="yes">_New log</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image3">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-new</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="file_configure">
+                        <property name="label" translatable="yes">_Configure Log</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">False</property>
+                        <signal name="activate" handler="gtk_widget_show" object="file_chooser_dialog" after="yes"/>
+                        <child internal-child="image">
+                          <widget class="GtkImage" id="image4">
+                            <property name="visible">True</property>
+                            <property name="stock">gtk-preferences</property>
+                          </widget>
+                        </child>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem6">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Voice</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu6">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkCheckMenuItem" id="voice_enable">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Enable _Voice</property>
+                        <property name="use_underline">True</property>
+                        <property name="active">True</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="channel_menu_item">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Channel</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu7">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_0">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 0 (434.550MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_1">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 1 (434.650MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_2">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 2 (434.750MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_3">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 3 (434.850MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_4">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 4 (434.950MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_5">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 5 (435.050MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_6">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 6 (435.150MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_7">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 7 (435.250MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_8">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 8 (435.350MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                    <child>
+                      <widget class="GtkRadioMenuItem" id="channel_9">
+                        <property name="visible">True</property>
+                        <property name="label" translatable="yes">Channel 9 (435.450MHz)</property>
+                        <property name="use_underline">True</property>
+                        <property name="draw_as_radio">True</property>
+                        <property name="group">channel_0</property>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkMenuItem" id="menuitem4">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">_Help</property>
+                <property name="use_underline">True</property>
+                <child>
+                  <widget class="GtkMenu" id="menu3">
+                    <property name="visible">True</property>
+                    <child>
+                      <widget class="GtkImageMenuItem" id="imagemenuitem10">
+                        <property name="label">gtk-about</property>
+                        <property name="visible">True</property>
+                        <property name="use_underline">True</property>
+                        <property name="use_stock">True</property>
+                        <signal name="activate" handler="gtk_widget_show" object="about_dialog" after="yes"/>
+                      </widget>
+                    </child>
+                  </widget>
+                </child>
+              </widget>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkTable" id="table1">
+            <property name="visible">True</property>
+            <property name="n_rows">2</property>
+            <property name="n_columns">4</property>
+            <property name="row_spacing">3</property>
+            <property name="homogeneous">True</property>
+            <child>
+              <widget class="GtkLabel" id="height_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Height (m)</property>
+                <property name="justify">center</property>
+              </widget>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="state_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">State</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="rssi_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">RSSI (dBm)</property>
+              </widget>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="height_value">
+                <property name="visible">True</property>
+                <property name="ypad">2</property>
+                <property name="label" translatable="yes">0</property>
+                <property name="selectable">True</property>
+              </widget>
+              <packing>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="state_value">
+                <property name="visible">True</property>
+                <property name="ypad">2</property>
+                <property name="label" translatable="yes">pad</property>
+                <property name="selectable">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">1</property>
+                <property name="right_attach">2</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="rssi_value">
+                <property name="visible">True</property>
+                <property name="ypad">2</property>
+                <property name="label" translatable="yes">-50</property>
+                <property name="selectable">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">2</property>
+                <property name="right_attach">3</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="speed_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">Speed (m/s)</property>
+              </widget>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkLabel" id="speed_value">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">0</property>
+                <property name="selectable">True</property>
+              </widget>
+              <packing>
+                <property name="left_attach">3</property>
+                <property name="right_attach">4</property>
+                <property name="top_attach">1</property>
+                <property name="bottom_attach">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <widget class="GtkHBox" id="hbox1">
+            <property name="visible">True</property>
+            <child>
+              <widget class="GtkTreeView" id="dataview_0">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="show_expanders">False</property>
+                <property name="enable_grid_lines">both</property>
+              </widget>
+              <packing>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTreeView" id="dataview_1">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="show_expanders">False</property>
+                <property name="enable_grid_lines">both</property>
+              </widget>
+              <packing>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkTreeView" id="dataview_2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="show_expanders">False</property>
+                <property name="enable_grid_lines">both</property>
+              </widget>
+              <packing>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="position">2</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkDialog" id="device_connect_dialog">
+    <property name="border_width">5</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child>
+          <widget class="GtkTreeView" id="dev_list">
+            <property name="width_request">300</property>
+            <property name="height_request">100</property>
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="headers_clickable">False</property>
+            <property name="rules_hint">True</property>
+            <property name="search_column">0</property>
+            <property name="show_expanders">False</property>
+            <property name="level_indentation">1</property>
+            <property name="enable_grid_lines">both</property>
+            <property name="enable_tree_lines">True</property>
+          </widget>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <widget class="GtkButton" id="cancel_button">
+                <property name="label">gtk-cancel</property>
+                <property name="response_id">1</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_underline">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="gtk_widget_hide" object="device_connect_dialog" after="yes"/>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="connect_button">
+                <property name="label">gtk-connect</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkFileChooserDialog" id="file_chooser_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Configure Log Directory</property>
+    <property name="type_hint">dialog</property>
+    <property name="has_separator">False</property>
+    <property name="action">select-folder</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox2">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area2">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <widget class="GtkButton" id="file_configure_cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="gtk_widget_hide" object="file_chooser_dialog"/>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="file_configure_ok">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="can_default">True</property>
+                <property name="has_default">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkMessageDialog" id="file_fail_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Failed to create log</property>
+    <property name="type_hint">normal</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="transient_for">aoview</property>
+    <property name="message_type">error</property>
+    <property name="buttons">close</property>
+    <property name="text">Cannot create log file</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox4">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area4">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkMessageDialog" id="dev_open_fail_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">Failed to open device</property>
+    <property name="type_hint">normal</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="transient_for">aoview</property>
+    <property name="message_type">error</property>
+    <property name="buttons">close</property>
+    <property name="text">Cannot open device</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox6">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area6">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkAboutDialog" id="about_dialog">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">About AoView</property>
+    <property name="resizable">False</property>
+    <property name="type_hint">normal</property>
+    <property name="transient_for">aoview</property>
+    <property name="has_separator">False</property>
+    <property name="program_name">AoView</property>
+    <property name="copyright" translatable="yes">Copyright &#xA9; 2009 Keith Packard</property>
+    <property name="comments" translatable="yes">AltOS data capture and display.</property>
+    <property name="website">http://altusmetrum.org</property>
+    <property name="license" translatable="yes">AoView is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License.
+
+AoView is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with AoView; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</property>
+    <property name="authors">Keith Packard &lt;keithp@keithp.com&gt;</property>
+    <property name="wrap_license">True</property>
+    <signal name="close" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
+    <signal name="response" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox7">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area7">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkMessageDialog" id="ao_save_done">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes">EEPROM save complete</property>
+    <property name="type_hint">normal</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="transient_for">aoview</property>
+    <property name="buttons">close</property>
+    <property name="text">Saving EEPROM data as</property>
+    <property name="secondary_text">&lt;filename&gt;</property>
+    <signal name="close" handler="gtk_widget_hide" object="ao_save_done"/>
+    <signal name="response" handler="gtk_widget_hide" object="ao_save_done"/>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox11">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area11">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+  <widget class="GtkFileChooserDialog" id="ao_replay_dialog">
+    <property name="border_width">5</property>
+    <property name="destroy_with_parent">True</property>
+    <property name="type_hint">dialog</property>
+    <property name="skip_taskbar_hint">True</property>
+    <property name="transient_for">aoview</property>
+    <property name="has_separator">False</property>
+    <child internal-child="vbox">
+      <widget class="GtkVBox" id="dialog-vbox10">
+        <property name="visible">True</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <widget class="GtkHButtonBox" id="dialog-action_area10">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <widget class="GtkButton" id="ao_replay_cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+                <signal name="clicked" handler="gtk_widget_hide" object="ao_replay_dialog"/>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <widget class="GtkButton" id="ao_replay_ok">
+                <property name="label">gtk-ok</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </widget>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </widget>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </widget>
+    </child>
+  </widget>
+</glade-interface>
diff --git a/ao-tools/ao-view/aoview.h b/ao-tools/ao-view/aoview.h
new file mode 100644 (file)
index 0000000..b937df7
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AOVIEW_H_
+#define _AOVIEW_H_
+
+#define _GNU_SOURCE
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <assert.h>
+#include <math.h>
+
+#include "cc.h"
+
+#include <gtk/gtk.h>
+#include <glade/glade.h>
+#include <gconf/gconf-client.h>
+
+struct aostate {
+       struct cc_telem data;
+
+       /* derived data */
+
+       struct cc_telem prev_data;
+
+       double          report_time;
+
+       gboolean        ascent; /* going up? */
+
+       int     ground_altitude;
+       int     height;
+       double  speed;
+       double  acceleration;
+       double  battery;
+       double  temperature;
+       double  main_sense;
+       double  drogue_sense;
+       double  baro_speed;
+
+       int     max_height;
+       double  max_acceleration;
+       double  max_speed;
+
+       struct cc_gps   gps;
+       struct cc_gps_tracking  gps_tracking;
+
+       int     gps_valid;
+       double  pad_lat;
+       double  pad_lon;
+       double  pad_alt;
+       double  pad_lat_total;
+       double  pad_lon_total;
+       double  pad_alt_total;
+       int     npad;
+       int     prev_npad;
+
+       double  distance;
+       double  bearing;
+       int     gps_height;
+
+       int     speak_tick;
+       int     speak_altitude;
+};
+
+extern struct aostate aostate;
+
+/* GPS is 'stable' when we've seen at least this many samples */
+#define MIN_PAD_SAMPLES        10
+
+void
+aoview_monitor_disconnect(void);
+
+gboolean
+aoview_monitor_connect(char *tty);
+
+gboolean
+aoview_monitor_parse(const char *line);
+
+void
+aoview_monitor_set_channel(int channel);
+
+void
+aoview_monitor_reset(void);
+
+struct aoview_serial *
+aoview_serial_open(const char *tty);
+
+void
+aoview_serial_close(struct aoview_serial *serial);
+
+typedef void (*aoview_serial_callback)(gpointer user_data, struct aoview_serial *serial, gint revents);
+
+void
+aoview_serial_set_callback(struct aoview_serial *serial,
+                          aoview_serial_callback func);
+
+void
+aoview_serial_printf(struct aoview_serial *serial, char *format, ...);
+
+int
+aoview_serial_read(struct aoview_serial *serial, char *buf, int len);
+
+int
+aoview_serial_getc(struct aoview_serial *serial);
+
+void
+aoview_dev_dialog_init(GladeXML *xml);
+
+void
+aoview_state_notify(struct cc_telem *data);
+
+void
+aoview_state_new(void);
+
+void
+aoview_state_init(GladeXML *xml);
+
+int16_t
+aoview_pres_to_altitude(int16_t pres);
+
+int16_t
+aoview_altitude_to_pres(int16_t alt);
+
+char *
+aoview_fullname (char *dir, char *file);
+
+char *
+aoview_basename(char *file);
+
+GtkTreeViewColumn *
+aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width);
+
+int
+aoview_mkdir(char *dir);
+
+void
+aoview_log_init(GladeXML *xml);
+
+void
+aoview_log_set_serial(int serial);
+
+int
+aoview_log_get_serial(void);
+
+void
+aoview_log_printf(char *format, ...);
+
+void
+aoview_log_new(void);
+
+void
+aoview_table_start(void);
+
+void
+aoview_table_add_row(int column, char *label, char *format, ...);
+
+void
+aoview_table_finish(void);
+
+void
+aoview_table_init(GladeXML *xml);
+
+void
+aoview_table_clear(void);
+
+struct aoview_file;
+
+extern char *aoview_file_dir;
+
+void
+aoview_file_finish(struct aoview_file *file);
+
+gboolean
+aoview_file_start(struct aoview_file *file);
+
+const char *
+aoview_file_name(struct aoview_file *file);
+
+void
+aoview_file_set_serial(struct aoview_file *file, int serial);
+
+int
+aoview_file_get_serial(struct aoview_file *file);
+
+void
+aoview_file_printf(struct aoview_file *file, char *format, ...);
+
+void
+aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap);
+
+struct aoview_file *
+aoview_file_new(char *ext);
+
+void
+aoview_file_destroy(struct aoview_file *file);
+
+void
+aoview_file_init(GladeXML *xml);
+
+/* aoview_eeprom.c */
+
+gboolean
+aoview_eeprom_save(const char *device);
+
+void
+aoview_eeprom_init(GladeXML *xml);
+
+/* aoview_voice.c */
+void aoview_voice_open(void);
+
+void aoview_voice_close(void);
+
+void aoview_voice_speak(char *format, ...);
+
+/* aoview_label.c */
+
+void aoview_label_init(GladeXML *xml);
+
+void
+aoview_label_show(struct aostate *state);
+
+/* aoview_flite.c */
+
+FILE *
+aoview_flite_start(void);
+
+void
+aoview_flite_stop(void);
+
+/* aoview_main.c */
+
+extern char *aoview_tty;
+
+/* aoview_channel.c */
+
+int
+aoview_channel_current(void);
+
+void
+aoview_channel_init(GladeXML *xml);
+
+#endif /* _AOVIEW_H_ */
diff --git a/ao-tools/ao-view/aoview_channel.c b/ao-tools/ao-view/aoview_channel.c
new file mode 100644 (file)
index 0000000..959173c
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+
+#define NUM_CHANNEL    10
+
+static GtkRadioMenuItem *channel_item[NUM_CHANNEL];
+
+int
+aoview_channel_current(void)
+{
+       int     c;
+
+       for (c = 0; c < NUM_CHANNEL; c++)
+               if(gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(channel_item[c])))
+                       return c;
+       return -1;
+}
+
+static void
+aoview_channel_notify(int channel)
+{
+       if (0 <= channel && channel < NUM_CHANNEL)
+               gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(channel_item[channel]), TRUE);
+}
+
+#define ALTOS_CHANNEL_PATH     "/apps/aoview/channel"
+
+static void
+aoview_channel_change(GtkWidget *widget, gpointer data)
+{
+       gboolean        enabled = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+       int             c = (int) data;
+       GConfClient     *gconf_client;
+       GError          *error;
+
+       if (enabled) {
+               aoview_monitor_set_channel(c);
+               gconf_client = gconf_client_get_default();
+               gconf_client_set_int(gconf_client, ALTOS_CHANNEL_PATH, c, &error);
+       }
+}
+
+void
+aoview_channel_init(GladeXML *xml)
+{
+       int     c;
+       GConfClient     *gconf_client;
+
+       for (c = 0; c < NUM_CHANNEL; c++) {
+               char    name[32];
+
+               sprintf(name, "channel_%d", c);
+               channel_item[c] = GTK_RADIO_MENU_ITEM(glade_xml_get_widget(xml, name));
+               assert(channel_item[c]);
+               g_signal_connect(G_OBJECT(channel_item[c]), "toggled",
+                                G_CALLBACK(aoview_channel_change),
+                                (gpointer) c);
+       }
+       gconf_client = gconf_client_get_default();
+       c = 0;
+       if (gconf_client)
+       {
+               GError  *error;
+
+               error = NULL;
+               c = gconf_client_get_int(gconf_client,
+                                        ALTOS_CHANNEL_PATH,
+                                        &error);
+               if (error)
+                       c = 0;
+       }
+       aoview_channel_notify(c);
+}
diff --git a/ao-tools/ao-view/aoview_convert.c b/ao-tools/ao-view/aoview_convert.c
new file mode 100644 (file)
index 0000000..8ee05e1
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static int16_t altitude_table[] = {
+#include "altitude.h"
+};
+
+#define ALT_FRAC_SCALE (1 << ALT_FRAC_BITS)
+#define ALT_FRAC_MASK  (ALT_FRAC_SCALE - 1)
+
+int16_t
+aoview_pres_to_altitude(int16_t pres)
+{
+       uint8_t o;
+       int16_t part;
+
+       if (pres < 0)
+               pres = 0;
+       o = pres >> ALT_FRAC_BITS;
+       part = pres & ALT_FRAC_MASK;
+
+       return ((int32_t) altitude_table[o] * (ALT_FRAC_SCALE - part) +
+               (int32_t) altitude_table[o+1] * part + (ALT_FRAC_SCALE >> 1)) >> ALT_FRAC_BITS;
+}
+
+int16_t
+aoview_altitude_to_pres(int16_t alt)
+{
+       int16_t span, sub_span;
+       uint8_t l, h, m;
+       int32_t pres;
+
+       l = 0;
+       h = NALT - 1;
+       while ((h - l) != 1) {
+               m = (l + h) >> 1;
+               if (altitude_table[m] < alt)
+                       h = m;
+               else
+                       l = m;
+       }
+       span = altitude_table[l] - altitude_table[h];
+       sub_span = altitude_table[l] - alt;
+       pres = ((((int32_t) l * (span - sub_span) + (int32_t) h * sub_span) << ALT_FRAC_BITS) + (span >> 1)) / span;
+       if (pres > 32767)
+               pres = 32767;
+       if (pres < 0)
+               pres = 0;
+       return (int16_t) pres;
+}
diff --git a/ao-tools/ao-view/aoview_dev_dialog.c b/ao-tools/ao-view/aoview_dev_dialog.c
new file mode 100644 (file)
index 0000000..2ea4320
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static void
+aoview_dev_dialog_map(GtkWidget *widget, gpointer data)
+{
+       GtkTreeView     *dev_list = data;
+       GtkListStore    *list_store;
+       GtkTreeIter     iter;
+       int             ndev, n;
+       struct cc_usbdevs       *devs;
+       struct cc_usbdev        *dev;
+
+       list_store = gtk_list_store_new(3,
+                                       G_TYPE_STRING,
+                                       G_TYPE_INT,
+                                       G_TYPE_STRING);
+
+       devs = cc_usbdevs_scan();
+       if (devs) {
+               for (n = 0; n < devs->ndev; n++) {
+                       dev = devs->dev[n];
+                       gtk_list_store_append(list_store, &iter);
+                       gtk_list_store_set(list_store, &iter,
+                                          0, dev->product,
+                                          1, dev->serial,
+                                          2, dev->tty,
+                                          -1);
+               }
+       }
+       gtk_tree_view_set_model (dev_list, GTK_TREE_MODEL(list_store));
+       g_object_unref(G_OBJECT(list_store));
+       gtk_tree_view_columns_autosize(dev_list);
+       cc_usbdevs_free(devs);
+}
+
+static GtkMessageDialog *dev_open_fail_dialog;
+
+static void
+aoview_dev_open_failed(char *name)
+{
+       char    *utf8_file;
+       utf8_file = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
+       if (!utf8_file)
+               utf8_file = name;
+       gtk_message_dialog_format_secondary_text(dev_open_fail_dialog,
+                                                "\"%s\"", utf8_file);
+       if (utf8_file != name)
+               g_free(utf8_file);
+       gtk_dialog_run(GTK_DIALOG(dev_open_fail_dialog));
+       gtk_widget_hide(GTK_WIDGET(dev_open_fail_dialog));
+}
+
+gboolean       dialog_save_log;
+
+static void
+aoview_dev_selected(GtkTreeModel *model,
+                   GtkTreePath *path,
+                   GtkTreeIter *iter,
+                   gpointer data)
+{
+       gchar *string;
+       gtk_tree_model_get(model, iter,
+                          2, &string,
+                          -1);
+       if (dialog_save_log) {
+               dialog_save_log = FALSE;
+               if (!aoview_eeprom_save(string))
+                       aoview_dev_open_failed(string);
+       } else {
+               if (!aoview_monitor_connect(string))
+                       aoview_dev_open_failed(string);
+       }
+}
+
+static GtkWidget       *dialog;
+
+static void
+aoview_dev_dialog_connect(GtkWidget *widget, gpointer data)
+{
+       GtkTreeView             *dev_list = data;
+       GtkListStore            *list_store;
+       GtkTreeSelection        *tree_selection;
+
+       list_store = GTK_LIST_STORE(gtk_tree_view_get_model(dev_list));
+       tree_selection = gtk_tree_view_get_selection(dev_list);
+       gtk_tree_selection_selected_foreach(tree_selection,
+                                           aoview_dev_selected,
+                                           data);
+       gtk_widget_hide(dialog);
+}
+
+static void
+aoview_dev_disconnect(GtkWidget *widget)
+{
+       aoview_monitor_disconnect();
+}
+
+static void
+aoview_dev_savelog(GtkWidget *widget, gpointer data)
+{
+       dialog_save_log = TRUE;
+       gtk_widget_show(dialog);
+}
+
+#define _(a) a
+
+void
+aoview_dev_dialog_init(GladeXML *xml)
+{
+       GtkTreeView     *dev_list;
+       GtkWidget       *connect_button;
+       GtkTreeSelection        *dev_selection;
+       GtkWidget       *ao_disconnect;
+       GtkWidget       *ao_savelog;
+
+       dialog = glade_xml_get_widget(xml, "device_connect_dialog");
+       assert(dialog);
+
+       dev_list = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dev_list"));
+       assert(dev_list);
+
+       aoview_add_plain_text_column(dev_list, _("Product"), 0, 16);
+       aoview_add_plain_text_column(dev_list, _("Serial"),  1, 8);
+       aoview_add_plain_text_column(dev_list, _("Device"), 2, 13);
+
+       dev_selection = gtk_tree_view_get_selection(dev_list);
+       gtk_tree_selection_set_mode(dev_selection, GTK_SELECTION_SINGLE);
+
+       g_signal_connect(G_OBJECT(dialog), "map",
+                        G_CALLBACK(aoview_dev_dialog_map),
+                        dev_list);
+
+       connect_button = glade_xml_get_widget(xml, "connect_button");
+       assert(connect_button);
+
+       g_signal_connect(G_OBJECT(connect_button), "clicked",
+                        G_CALLBACK(aoview_dev_dialog_connect),
+                        dev_list);
+
+
+       ao_disconnect = glade_xml_get_widget(xml, "ao_disconnect");
+       assert(ao_disconnect);
+
+       g_signal_connect(G_OBJECT(ao_disconnect), "activate",
+                        G_CALLBACK(aoview_dev_disconnect),
+                        ao_disconnect);
+
+       ao_savelog = glade_xml_get_widget(xml, "ao_savelog");
+       assert(ao_savelog);
+
+       g_signal_connect(G_OBJECT(ao_savelog), "activate",
+                        G_CALLBACK(aoview_dev_savelog),
+                        dialog);
+       dev_open_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "dev_open_fail_dialog"));
+       assert(dev_open_fail_dialog);
+}
diff --git a/ao-tools/ao-view/aoview_eeprom.c b/ao-tools/ao-view/aoview_eeprom.c
new file mode 100644 (file)
index 0000000..447b83a
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+#define EEPROM_LEN     1024
+
+static struct aoview_file      *eeprom_file;
+static char                    eeprom_line[EEPROM_LEN + 1];
+static int                     eeprom_pos;
+static GtkMessageDialog                *eeprom_save_done;
+static GtkWidget               *eeprom_save_close;
+static gboolean                        eeprom_save_shown;
+
+static void
+aoview_eeprom_disconnect(struct aoview_serial *serial)
+{
+       aoview_file_finish(eeprom_file);
+}
+
+static void
+aoview_eeprom_done(struct aoview_serial *serial)
+{
+       gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
+                            "EEPROM data saved");
+       gtk_message_dialog_set_markup(eeprom_save_done,
+                                     "<b>EEPROM data saved as</b>");
+       if (!eeprom_save_shown)
+               gtk_widget_show(GTK_WIDGET(eeprom_save_done));
+       eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
+       if (eeprom_save_close)
+               gtk_widget_set_sensitive(eeprom_save_close, TRUE);
+       aoview_eeprom_disconnect(serial);
+}
+
+static gboolean
+aoview_eeprom_parse(struct aoview_serial *serial,
+                   char *line)
+{
+       char            cmd;
+       int             tick;
+       int             a;
+       int             b;
+       int             serial_number;
+       const char      *name;
+       char            *utf8_name;
+
+       if (!strcmp(line, "end")) {
+               aoview_eeprom_done(serial);
+               return FALSE;
+       }
+       if (sscanf(line, "serial-number %u", &serial_number) == 1) {
+               aoview_file_set_serial(eeprom_file, serial_number);
+       } else if (sscanf(line, "%c %x %x %x", &cmd, &tick, &a, &b) == 4) {
+               if (cmd == 'F')
+                       aoview_file_set_flight(eeprom_file, b);
+               aoview_file_printf(eeprom_file, "%s\n", line);
+               if (cmd == 'S' && a == 8) {
+                       aoview_eeprom_done(serial);
+                       return FALSE;
+               }
+
+               if (!eeprom_save_shown)
+               {
+                       name = aoview_file_name(eeprom_file);
+                       if (name) {
+                               utf8_name = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
+                               if (!utf8_name)
+                                       utf8_name = (char *) name;
+                               gtk_widget_set_sensitive(eeprom_save_close, FALSE);
+                               gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
+                                                    "Saving EEPROM data");
+                               gtk_message_dialog_set_markup(eeprom_save_done,
+                                                             "<b>Saving EEPROM data as</b>");
+                               gtk_message_dialog_format_secondary_text(eeprom_save_done, "%s",
+                                                                        utf8_name);
+                               if (utf8_name != name)
+                                       g_free(utf8_name);
+                               gtk_container_check_resize(GTK_CONTAINER(eeprom_save_done));
+                               gtk_widget_show(GTK_WIDGET(eeprom_save_done));
+                               eeprom_save_shown = TRUE;
+                               eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
+                               if (eeprom_save_close)
+                                       gtk_widget_set_sensitive(eeprom_save_close, FALSE);
+                       }
+               }
+       }
+       return TRUE;
+}
+
+static void
+aoview_eeprom_callback(gpointer user_data,
+                      struct aoview_serial *serial,
+                      gint revents)
+{
+       int     c;
+
+       if (revents & (G_IO_HUP|G_IO_ERR)) {
+               aoview_eeprom_disconnect(serial);
+               return;
+       }
+       if (revents & G_IO_IN) {
+               for (;;) {
+                       c = aoview_serial_getc(serial);
+                       if (c == -1)
+                               break;
+                       if (c == '\r')
+                               continue;
+                       if (c == '\n') {
+                               eeprom_line[eeprom_pos] = '\0';
+                               if (eeprom_pos)
+                               if (!aoview_eeprom_parse(serial, eeprom_line))
+                                       break;
+                               eeprom_pos = 0;
+                       } else if (eeprom_pos < EEPROM_LEN)
+                               eeprom_line[eeprom_pos++] = c;
+               }
+       }
+}
+
+gboolean
+aoview_eeprom_save(const char *device)
+{
+       struct aoview_serial    *serial;
+
+       gtk_widget_hide(GTK_WIDGET(eeprom_save_done));
+       eeprom_save_shown = FALSE;
+       serial = aoview_serial_open(device);
+       if (!serial)
+               return FALSE;
+       aoview_serial_set_callback(serial, aoview_eeprom_callback);
+       aoview_serial_printf(serial, "v\nl\n");
+       return TRUE;
+}
+
+void
+aoview_eeprom_init(GladeXML *xml)
+{
+       eeprom_file = aoview_file_new("eeprom");
+       assert(eeprom_file);
+
+       eeprom_save_done = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "ao_save_done"));
+       assert(eeprom_save_done);
+
+}
diff --git a/ao-tools/ao-view/aoview_file.c b/ao-tools/ao-view/aoview_file.c
new file mode 100644 (file)
index 0000000..292887a
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+char *aoview_file_dir;
+
+#define ALTOS_DIR_PATH "/apps/aoview/log_dir"
+#define DEFAULT_DIR    "AltOS"
+
+struct aoview_file {
+       char    *ext;
+       FILE    *file;
+       char    *name;
+       int     failed;
+       int     serial;
+       int     flight;
+       int     sequence;
+};
+
+static void
+aoview_file_save_conf(void)
+{
+       GConfClient     *gconf_client;
+
+       gconf_client = gconf_client_get_default();
+       if (gconf_client)
+       {
+               gconf_client_set_string(gconf_client,
+                                       ALTOS_DIR_PATH,
+                                       aoview_file_dir,
+                                       NULL);
+               g_object_unref(G_OBJECT(gconf_client));
+       }
+}
+
+static void
+aoview_file_configure(GtkWidget *widget, gpointer data)
+{
+       GtkFileChooser *chooser = data;
+       aoview_file_dir = gtk_file_chooser_get_filename(chooser);
+       aoview_file_save_conf();
+       gtk_widget_hide(GTK_WIDGET(chooser));
+}
+
+void
+aoview_file_finish(struct aoview_file *file)
+{
+       if (file->file) {
+               fclose(file->file);
+               file->file = NULL;
+               free(file->name);
+               file->name = NULL;
+       }
+       file->failed = 0;
+}
+
+const char *
+aoview_file_name(struct aoview_file *file)
+{
+       return file->name;
+}
+
+static GtkMessageDialog *file_fail_dialog;
+
+static void
+aoview_file_open_failed(char *name)
+{
+       char    *utf8_file;
+       utf8_file = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
+       if (!utf8_file)
+               utf8_file = name;
+       gtk_message_dialog_format_secondary_text(file_fail_dialog,
+                                                "\"%s\"", utf8_file);
+       if (utf8_file != name)
+               g_free(utf8_file);
+       gtk_widget_show(GTK_WIDGET(file_fail_dialog));
+}
+
+gboolean
+aoview_file_start(struct aoview_file *file)
+{
+       char            base[50];
+       char            seq[20];
+       struct tm       tm;
+       time_t          now;
+       char            *full;
+       int             r;
+
+       if (file->file)
+               return TRUE;
+
+       if (file->failed)
+               return FALSE;
+
+       full = cc_make_filename(file->serial, file->flight, file->ext);
+       file->file = fopen(full, "w");
+       if (!file->file) {
+               aoview_file_open_failed(full);
+               free(full);
+               file->failed = 1;
+               return FALSE;
+       } else {
+               setlinebuf(file->file);
+               file->name = full;
+               return TRUE;
+       }
+}
+
+void
+aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap)
+{
+       if (!aoview_file_start(file))
+               return;
+       vfprintf(file->file, format, ap);
+}
+
+void
+aoview_file_printf(struct aoview_file *file, char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       aoview_file_vprintf(file, format, ap);
+       va_end(ap);
+}
+
+struct aoview_file *
+aoview_file_new(char *ext)
+{
+       struct aoview_file      *file;
+
+       file = calloc (1, sizeof (struct aoview_file));
+       if (!file)
+               return NULL;
+       file->ext = strdup(ext);
+       if (!file->ext) {
+               free(file);
+               return NULL;
+       }
+       return file;
+}
+
+void
+aoview_file_destroy(struct aoview_file *file)
+{
+       if (file->file)
+               fclose(file->file);
+       if (file->name)
+               free(file->name);
+       free(file->ext);
+       free(file);
+}
+
+void
+aoview_file_set_serial(struct aoview_file *file, int serial)
+{
+       if (serial != file->serial)
+               aoview_file_finish(file);
+       file->serial = serial;
+}
+
+int
+aoview_file_get_serial(struct aoview_file *file)
+{
+       return file->serial;
+}
+
+void
+aoview_file_set_flight(struct aoview_file *file, int flight)
+{
+       if (flight != file->flight)
+               aoview_file_finish(file);
+       file->flight = flight;
+}
+
+int
+aoview_file_get_flight(struct aoview_file *file)
+{
+       return file->flight;
+}
+
+void
+aoview_file_init(GladeXML *xml)
+{
+       GConfClient     *gconf_client;
+       char            *file_dir = NULL;
+       GtkFileChooser  *file_chooser_dialog;
+       GtkWidget       *file_configure_ok;
+
+       g_type_init();
+       gconf_client = gconf_client_get_default();
+       if (gconf_client)
+       {
+               file_dir = gconf_client_get_string(gconf_client,
+                                                  ALTOS_DIR_PATH,
+                                                  NULL);
+               g_object_unref(G_OBJECT(gconf_client));
+       }
+       if (!file_dir) {
+               aoview_file_dir = aoview_fullname(getenv("HOME"), DEFAULT_DIR);
+               aoview_file_save_conf();
+       } else {
+               aoview_file_dir = strdup(file_dir);
+       }
+
+       file_chooser_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "file_chooser_dialog"));
+       assert(file_chooser_dialog);
+       gtk_file_chooser_set_filename(file_chooser_dialog, aoview_file_dir);
+
+       file_configure_ok = glade_xml_get_widget(xml, "file_configure_ok");
+       assert(file_configure_ok);
+
+       g_signal_connect(G_OBJECT(file_configure_ok), "clicked",
+                        G_CALLBACK(aoview_file_configure),
+                        file_chooser_dialog);
+
+
+       file_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "file_fail_dialog"));
+       assert(file_fail_dialog);
+}
diff --git a/ao-tools/ao-view/aoview_flite.c b/ao-tools/ao-view/aoview_flite.c
new file mode 100644 (file)
index 0000000..abcdc49
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdio.h>
+#include <flite/flite.h>
+#include "aoview.h"
+#include <alsa/asoundlib.h>
+
+cst_voice *register_cmu_us_kal16();
+cst_voice *register_cmu_us_kal();
+
+static cst_voice *voice;
+
+static FILE *pipe_write;
+static GThread *aoview_flite_thread;
+
+static snd_pcm_t       *alsa_handle;
+
+gpointer
+aoview_flite_task(gpointer data)
+{
+       FILE            *input = data;
+       char            line[1024];
+       cst_wave        *wave;
+       int             rate;
+       int             channels;
+       int             err;
+       char            *samples;
+       int             num_samples;
+
+       err = snd_pcm_open(&alsa_handle, "default",
+                          SND_PCM_STREAM_PLAYBACK, 0);
+       if (err < 0) {
+               fprintf(stderr, "alsa open failed %s\n",
+                       strerror(-err));
+               alsa_handle = NULL;
+       }
+       rate = 0;
+       channels = 0;
+       while (fgets(line, sizeof (line) - 1, input) != NULL) {
+               if (!alsa_handle)
+                       continue;
+               wave = flite_text_to_wave(line, voice);
+               if (wave->sample_rate != rate ||
+                   wave->num_channels != channels)
+               {
+                       rate = wave->sample_rate;
+                       channels = wave->num_channels;
+                       err = snd_pcm_set_params(alsa_handle,
+                                                SND_PCM_FORMAT_S16,
+                                                SND_PCM_ACCESS_RW_INTERLEAVED,
+                                                channels,
+                                                rate,
+                                                1,
+                                                100000);
+                       if (err < 0)
+                               fprintf(stderr, "alsa set_params error %s\n",
+                                       strerror(-err));
+               }
+               err = snd_pcm_prepare(alsa_handle);
+               if (err < 0)
+                       fprintf(stderr, "alsa pcm_prepare error %s\n",
+                               strerror(-err));
+               samples = (char *) wave->samples;
+               num_samples = wave->num_samples;
+               while (num_samples > 0) {
+                       err = snd_pcm_writei(alsa_handle,
+                                            samples, num_samples);
+                       if (err <= 0) {
+                               fprintf(stderr, "alsa write error %s\n",
+                                       strerror(-err));
+                               break;
+                       }
+                       num_samples -= err;
+                       samples += err * 2 * channels;
+               }
+               snd_pcm_drain(alsa_handle);
+               delete_wave(wave);
+       }
+       snd_pcm_close(alsa_handle);
+       alsa_handle = 0;
+       return NULL;
+}
+
+void
+aoview_flite_stop(void)
+{
+       int status;
+       if (pipe_write) {
+               fclose(pipe_write);
+               pipe_write = NULL;
+       }
+       if (aoview_flite_thread) {
+               g_thread_join(aoview_flite_thread);
+               aoview_flite_thread = NULL;
+       }
+}
+
+FILE *
+aoview_flite_start(void)
+{
+       static once;
+       int     p[2];
+       GError  *error;
+       FILE    *pipe_read;
+
+       if (!once) {
+               flite_init();
+#if HAVE_REGISTER_CMU_US_KAL16
+               voice = register_cmu_us_kal16();
+#else
+#if HAVE_REGISTER_CMU_US_KAL
+               voice = register_cmu_us_kal();
+#endif
+#endif
+               if (!voice) {
+                       perror("register voice");
+                       exit(1);
+               }
+       }
+       aoview_flite_stop();
+       pipe(p);
+       pipe_read = fdopen(p[0], "r");
+       pipe_write = fdopen(p[1], "w");
+       g_thread_create(aoview_flite_task, pipe_read, TRUE, &error);
+       return pipe_write;
+}
diff --git a/ao-tools/ao-view/aoview_label.c b/ao-tools/ao-view/aoview_label.c
new file mode 100644 (file)
index 0000000..2431362
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static struct {
+       char            *name;
+       char            *initial_value;
+       GtkLabel        *widget;
+} label_widgets[] = {
+       { "height_label", "Height (m)", NULL },
+       { "state_label", "State", NULL },
+       { "rssi_label", "RSSI (dBm)", NULL },
+       { "speed_label", "Speed (m/s)", NULL },
+       { "height_value", "0", NULL },
+       { "state_value", "pad", NULL },
+       { "rssi_value", "-50", NULL },
+       { "speed_value", "0", NULL },
+};
+
+static void
+aoview_label_assign(GtkLabel *widget, char *value)
+{
+       char    *markup;
+
+       markup = g_markup_printf_escaped("<span font_weight=\"bold\" size=\"xx-large\">%s</span>", value);
+       gtk_label_set_markup(widget, markup);
+       g_free(markup);
+}
+
+void
+aoview_label_show(struct aostate *state)
+{
+       char    line[1024];
+       sprintf(line, "%d", state->height);
+       aoview_label_assign(label_widgets[4].widget, line);
+
+       aoview_label_assign(label_widgets[5].widget, state->data.state);
+
+       sprintf(line, "%d", state->data.rssi);
+       aoview_label_assign(label_widgets[6].widget, line);
+
+       if (state->ascent)
+               sprintf(line, "%6.0f", fabs(state->speed));
+       else
+               sprintf(line, "%6.0f", fabs(state->baro_speed));
+       aoview_label_assign(label_widgets[7].widget, line);
+}
+
+void
+aoview_label_init(GladeXML *xml)
+{
+       int i;
+       for (i = 0; i < sizeof(label_widgets)/sizeof(label_widgets[0]); i++) {
+               label_widgets[i].widget = GTK_LABEL(glade_xml_get_widget(xml, label_widgets[i].name));
+               aoview_label_assign(label_widgets[i].widget, label_widgets[i].initial_value);
+               assert(label_widgets[i].widget);
+       }
+}
diff --git a/ao-tools/ao-view/aoview_log.c b/ao-tools/ao-view/aoview_log.c
new file mode 100644 (file)
index 0000000..2880ecb
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static struct aoview_file      *aoview_log;
+
+void
+aoview_log_new(void)
+{
+       aoview_file_finish(aoview_log);
+       aoview_state_new();
+}
+
+void
+aoview_log_set_serial(int serial)
+{
+       aoview_file_set_serial(aoview_log, serial);
+}
+
+int
+aoview_log_get_serial(void)
+{
+       return aoview_file_get_serial(aoview_log);
+}
+
+void
+aoview_log_set_flight(int flight)
+{
+       aoview_file_set_flight(aoview_log, flight);
+}
+
+int
+aoview_log_get_flight(void)
+{
+       return aoview_file_get_flight(aoview_log);
+}
+
+void
+aoview_log_printf(char *format, ...)
+{
+       va_list ap;
+
+       va_start(ap, format);
+       aoview_file_vprintf(aoview_log, format, ap);
+       va_end(ap);
+}
+
+static void
+aoview_log_new_item(GtkWidget *widget, gpointer data)
+{
+       aoview_file_finish(aoview_log);
+}
+
+void
+aoview_log_init(GladeXML *xml)
+{
+       GtkWidget       *log_new;
+
+       aoview_log = aoview_file_new("telem");
+       assert(aoview_log);
+
+       log_new = glade_xml_get_widget(xml, "log_new");
+       assert(log_new);
+       g_signal_connect(G_OBJECT(log_new), "activate",
+                        G_CALLBACK(aoview_log_new_item),
+                        NULL);
+}
diff --git a/ao-tools/ao-view/aoview_main.c b/ao-tools/ao-view/aoview_main.c
new file mode 100644 (file)
index 0000000..f8704b8
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static const char aoview_glade[] = {
+#include "aoview_glade.h"
+};
+
+static void usage(void) {
+       printf("aoview [--device|-d device_file]");
+       exit(1);
+}
+
+static void destroy_event(GtkWidget *widget, gpointer data)
+{
+       gtk_main_quit();
+}
+
+char *aoview_tty = NULL;
+
+int main(int argc, char **argv)
+{
+       GladeXML *xml = NULL;
+       GtkWidget *mainwindow;
+       GtkAboutDialog *about_dialog;
+
+       static struct option long_options[] = {
+               { "tty", 1, 0, 'T'},
+               { 0, 0, 0, 0 }
+       };
+       for (;;) {
+               int c, temp;
+
+               c = getopt_long_only(argc, argv, "T:", long_options, &temp);
+               if (c == -1)
+                       break;
+
+               switch (c) {
+               case 'T':
+                       aoview_tty = optarg;
+                       break;
+               default:
+                       usage();
+               }
+       }
+
+       g_thread_init(NULL);
+       gtk_init(&argc, &argv);
+       glade_init();
+
+       xml = glade_xml_new_from_buffer(aoview_glade, sizeof (aoview_glade), NULL, NULL);
+
+       /* connect the signals in the interface */
+       glade_xml_signal_autoconnect(xml);
+
+       /* Hook up the close button. */
+       mainwindow = glade_xml_get_widget(xml, "aoview");
+       assert(mainwindow);
+
+       g_signal_connect (G_OBJECT(mainwindow), "destroy",
+           G_CALLBACK(destroy_event), NULL);
+
+       about_dialog = GTK_ABOUT_DIALOG(glade_xml_get_widget(xml, "about_dialog"));
+       assert(about_dialog);
+       gtk_about_dialog_set_version(about_dialog, AOVIEW_VERSION);
+
+       aoview_voice_init(xml);
+
+       aoview_channel_init(xml);
+
+       aoview_dev_dialog_init(xml);
+
+       aoview_state_init(xml);
+
+       aoview_file_init(xml);
+
+       aoview_log_init(xml);
+
+       aoview_table_init(xml);
+
+       aoview_eeprom_init(xml);
+
+       aoview_replay_init(xml);
+
+       aoview_label_init(xml);
+
+       if (aoview_tty) {
+               if (!aoview_monitor_connect(aoview_tty)) {
+                       perror(aoview_tty);
+                       exit(1);
+               }
+       }
+       aoview_voice_speak("rocket flight monitor ready\n");
+
+       gtk_main();
+
+       return 0;
+}
diff --git a/ao-tools/ao-view/aoview_monitor.c b/ao-tools/ao-view/aoview_monitor.c
new file mode 100644 (file)
index 0000000..1f9937b
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static struct aoview_serial *monitor_serial;
+
+#define MONITOR_LEN    1024
+
+static char    monitor_line[MONITOR_LEN + 1];
+static int     monitor_pos;
+
+void
+aoview_monitor_disconnect(void)
+{
+       if (monitor_serial) {
+               aoview_serial_close(monitor_serial);
+               monitor_serial = NULL;
+       }
+       aoview_log_new();
+}
+
+gboolean
+aoview_monitor_parse(const char *input_line)
+{
+       struct cc_telem telem;
+
+       if (!cc_telem_parse(input_line, &telem))
+               return FALSE;
+       aoview_state_notify(&telem);
+       return TRUE;
+}
+
+static void
+aoview_monitor_callback(gpointer user_data,
+                       struct aoview_serial *serial,
+                       gint revents)
+{
+       int     c;
+
+       if (revents & (G_IO_HUP|G_IO_ERR)) {
+               aoview_monitor_disconnect();
+               return;
+       }
+       if (revents & G_IO_IN) {
+               for (;;) {
+                       c = aoview_serial_getc(serial);
+                       if (c == -1)
+                               break;
+                       if (c == '\r')
+                               continue;
+                       if (c == '\n') {
+                               monitor_line[monitor_pos] = '\0';
+                               if (monitor_pos) {
+                                       if (aoview_monitor_parse(monitor_line)) {
+                                               aoview_log_set_serial(aostate.data.serial);
+                                               aoview_log_set_flight(aostate.data.flight);
+                                               if (aoview_log_get_serial())
+                                                       aoview_log_printf ("%s\n", monitor_line);
+                                       }
+                               }
+                               monitor_pos = 0;
+                       } else if (monitor_pos < MONITOR_LEN)
+                               monitor_line[monitor_pos++] = c;
+               }
+       }
+}
+
+void
+aoview_monitor_set_channel(int channel)
+{
+       if (monitor_serial) {
+               aoview_serial_printf(monitor_serial, "m 0\n");
+               aoview_serial_printf(monitor_serial, "c r %d\n", channel);
+               aoview_serial_printf(monitor_serial, "m 1\n");
+       }
+}
+
+gboolean
+aoview_monitor_connect(char *tty)
+{
+       int     channel;
+       aoview_monitor_disconnect();
+       monitor_serial = aoview_serial_open(tty);
+       if (!monitor_serial)
+               return FALSE;
+       aoview_table_clear();
+       aoview_state_reset();
+       channel = aoview_channel_current();
+       aoview_monitor_set_channel(channel);
+       aoview_serial_set_callback(monitor_serial,
+                                  aoview_monitor_callback);
+       return TRUE;
+}
diff --git a/ao-tools/ao-view/aoview_replay.c b/ao-tools/ao-view/aoview_replay.c
new file mode 100644 (file)
index 0000000..da7b5d6
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+static GtkFileChooser  *replay_dialog;
+static GtkWidget       *replay_ok;
+static FILE            *replay_file;
+static int             replay_tick;
+
+static int
+find_tick(char *line, gboolean *is_pad)
+{
+       char    *state = strstr(line, "STATE");
+       if (!state)
+               return -1;
+       state = strchr(state, ' ');
+       if (!state)
+               return -1;
+       while (*state == ' ')
+               state++;
+       *is_pad = strncmp(state, "pad", 3) == 0;
+       while (*state && !isdigit(*state))
+               state++;
+       return atoi(state);
+}
+
+static void
+aoview_replay_close(void)
+{
+       if (replay_file) {
+               fclose(replay_file);
+               replay_file = NULL;
+       }
+}
+
+static char    replay_line[1024];
+
+static gboolean
+aoview_replay_read(gpointer data);
+
+static gboolean
+aoview_replay_execute(gpointer data)
+{
+       aoview_monitor_parse(replay_line);
+       g_idle_add(aoview_replay_read, NULL);
+       return FALSE;
+}
+
+static gboolean
+aoview_replay_read(gpointer data)
+{
+       int             tick;
+       gboolean        is_pad;
+
+       if (!replay_file)
+               return FALSE;
+       if (fgets(replay_line, sizeof (replay_line), replay_file)) {
+               tick = find_tick(replay_line, &is_pad);
+               if (tick >= 0 && replay_tick >= 0 && !is_pad) {
+                       while (tick < replay_tick)
+                               tick += 65536;
+                       g_timeout_add((tick - replay_tick) * 10,
+                                     aoview_replay_execute,
+                                     NULL);
+               } else {
+                       aoview_replay_execute(NULL);
+               }
+               replay_tick = tick;
+       } else {
+               aoview_replay_close();
+       }
+       return FALSE;
+}
+
+static void
+aoview_replay_open(GtkWidget *widget, gpointer data)
+{
+       char            *replay_file_name;
+       GtkWidget       *dialog;
+
+       aoview_replay_close();
+       replay_file_name = gtk_file_chooser_get_filename(replay_dialog);
+       replay_file = fopen(replay_file_name, "r");
+       if (!replay_file) {
+               dialog = gtk_message_dialog_new(GTK_WINDOW(replay_dialog),
+                                               GTK_DIALOG_DESTROY_WITH_PARENT,
+                                               GTK_MESSAGE_ERROR,
+                                               GTK_BUTTONS_CLOSE,
+                                               "Error loading file '%s': %s",
+                                               replay_file_name, g_strerror(errno));
+               gtk_dialog_run(GTK_DIALOG(dialog));
+               gtk_widget_destroy(dialog);
+       } else {
+               replay_tick = -1;
+               aoview_state_reset();
+               aoview_replay_read(NULL);
+       }
+       gtk_widget_hide(GTK_WIDGET(replay_dialog));
+}
+
+void
+aoview_replay_init(GladeXML *xml)
+{
+       GtkFileFilter   *telem_filter;
+       GtkFileFilter   *all_filter;
+       GtkFileFilter   *log_filter;
+
+       telem_filter = gtk_file_filter_new();
+       gtk_file_filter_add_pattern(telem_filter, "*.telem");
+       gtk_file_filter_set_name(telem_filter, "Telemetry Files");
+
+       log_filter = gtk_file_filter_new();
+       gtk_file_filter_add_pattern(log_filter, "*.log");
+       gtk_file_filter_set_name(log_filter, "Log Files");
+
+       all_filter = gtk_file_filter_new();
+       gtk_file_filter_add_pattern(all_filter, "*");
+       gtk_file_filter_set_name(all_filter, "All Files");
+
+       replay_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "ao_replay_dialog"));
+       assert(replay_dialog);
+       gtk_file_chooser_set_current_folder(replay_dialog, aoview_file_dir);
+       gtk_file_chooser_add_filter(replay_dialog, telem_filter);
+       gtk_file_chooser_add_filter(replay_dialog, log_filter);
+       gtk_file_chooser_add_filter(replay_dialog, all_filter);
+
+       replay_ok = glade_xml_get_widget(xml, "ao_replay_ok");
+       assert(replay_ok);
+       g_signal_connect(G_OBJECT(replay_ok), "clicked",
+                        G_CALLBACK(aoview_replay_open),
+                        replay_dialog);
+}
diff --git a/ao-tools/ao-view/aoview_serial.c b/ao-tools/ao-view/aoview_serial.c
new file mode 100644 (file)
index 0000000..29038b7
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+#include <termios.h>
+
+#define AOVIEW_SERIAL_IN_BUF   64
+#define AOVIEW_SERIAL_OUT_BUF  64
+
+struct aoview_buf {
+       char            *buf;
+       int             off;
+       int             count;
+       int             size;
+};
+
+static int
+aoview_buf_write(struct aoview_buf *buf, char *data, int len)
+{
+       if (buf->count + len > buf->size) {
+               int     new_size = buf->size * 2;
+               if (new_size == 0)
+                       new_size = 1024;
+               if (buf->buf)
+                       buf->buf = realloc (buf->buf, new_size);
+               else
+                       buf->buf = malloc (new_size);
+               buf->size = new_size;
+       }
+       memcpy(buf->buf + buf->count, data, len);
+       buf->count += len;
+       return len;
+}
+
+static int
+aoview_buf_read(struct aoview_buf *buf, char *data, int len)
+{
+       if (len > buf->count - buf->off)
+               len = buf->count - buf->off;
+       memcpy (data, buf->buf + buf->off, len);
+       buf->off += len;
+       if (buf->off == buf->count)
+               buf->off = buf->count = 0;
+       return len;
+}
+
+static int
+aoview_buf_getc(struct aoview_buf *buf)
+{
+       char    b;
+       int     r;
+
+       r = aoview_buf_read(buf, &b, 1);
+       if (r == 1)
+               return (int) b;
+       return -1;
+}
+
+static void
+aoview_buf_flush(struct aoview_buf *buf, int fd)
+{
+       int     ret;
+
+       if (buf->count > buf->off) {
+               ret = write(fd, buf->buf + buf->off, buf->count - buf->off);
+               if (ret > 0) {
+                       buf->off += ret;
+                       if (buf->off == buf->count)
+                               buf->off = buf->count = 0;
+               }
+       }
+}
+
+static void
+aoview_buf_fill(struct aoview_buf *buf, int fd)
+{
+       int ret;
+
+       while (buf->count >= buf->size) {
+               int new_size = buf->size * 2;
+               buf->buf = realloc (buf->buf, new_size);
+               buf->size = new_size;
+       }
+
+       ret = read(fd, buf->buf + buf->count, buf->size - buf->count);
+       if (ret > 0)
+               buf->count += ret;
+}
+
+static void
+aoview_buf_init(struct aoview_buf *buf)
+{
+       buf->buf = malloc (buf->size = 1024);
+       buf->count = 0;
+}
+
+static void
+aoview_buf_fini(struct aoview_buf *buf)
+{
+       free(buf->buf);
+}
+
+struct aoview_serial {
+       GSource                 source;
+       int                     fd;
+       struct termios          save_termios;
+       struct aoview_buf       in_buf;
+       struct aoview_buf       out_buf;
+       GPollFD                 poll_fd;
+};
+
+
+void
+aoview_serial_printf(struct aoview_serial *serial, char *format, ...)
+{
+       char    buf[1024];
+       va_list ap;
+       int     ret;
+
+       /* sprintf to a local buffer */
+       va_start(ap, format);
+       ret = vsnprintf(buf, sizeof(buf), format, ap);
+       va_end(ap);
+       if (ret > sizeof(buf)) {
+               fprintf(stderr, "printf overflow for format %s\n",
+                       format);
+       }
+
+       /* flush local buffer to the wire */
+       aoview_buf_write(&serial->out_buf, buf, ret);
+       aoview_buf_flush(&serial->out_buf, serial->fd);
+}
+
+int
+aoview_serial_read(struct aoview_serial *serial, char *buf, int len)
+{
+       return aoview_buf_read(&serial->in_buf, buf, len);
+}
+
+int
+aoview_serial_getc(struct aoview_serial *serial)
+{
+       return aoview_buf_getc(&serial->in_buf);
+}
+
+static gboolean
+serial_prepare(GSource *source, gint *timeout)
+{
+       struct aoview_serial *serial = (struct aoview_serial *) source;
+       *timeout = -1;
+
+       if (serial->out_buf.count)
+               serial->poll_fd.events |= G_IO_OUT;
+       else
+               serial->poll_fd.events &= ~G_IO_OUT;
+       return FALSE;
+}
+
+static gboolean
+serial_check(GSource *source)
+{
+       struct aoview_serial *serial = (struct aoview_serial *) source;
+       gint revents = serial->poll_fd.revents;
+
+       if (revents & G_IO_NVAL)
+               return FALSE;
+       if (revents & G_IO_IN)
+               return TRUE;
+       if (revents & G_IO_OUT)
+               return TRUE;
+       return FALSE;
+}
+
+static gboolean
+serial_dispatch(GSource *source,
+               GSourceFunc callback,
+               gpointer user_data)
+{
+       struct aoview_serial *serial = (struct aoview_serial *) source;
+       aoview_serial_callback func = (aoview_serial_callback) callback;
+       gint revents = serial->poll_fd.revents;
+
+       if (revents & G_IO_IN)
+               aoview_buf_fill(&serial->in_buf, serial->fd);
+
+       if (revents & G_IO_OUT)
+               aoview_buf_flush(&serial->out_buf, serial->fd);
+
+       if (func)
+               (*func)(user_data, serial, revents);
+       return TRUE;
+}
+
+static void
+serial_finalize(GSource *source)
+{
+       struct aoview_serial *serial = (struct aoview_serial *) source;
+
+       aoview_buf_fini(&serial->in_buf);
+       aoview_buf_fini(&serial->out_buf);
+       tcsetattr(serial->fd, TCSAFLUSH, &serial->save_termios);
+       close (serial->fd);
+}
+
+static GSourceFuncs serial_funcs = {
+       serial_prepare,
+       serial_check,
+       serial_dispatch,
+       serial_finalize
+};
+
+struct aoview_serial *
+aoview_serial_open(const char *tty)
+{
+       struct aoview_serial    *serial;
+       struct termios  termios;
+
+       serial = (struct aoview_serial *) g_source_new(&serial_funcs, sizeof (struct aoview_serial));
+       aoview_buf_init(&serial->in_buf);
+       aoview_buf_init(&serial->out_buf);
+       serial->fd = open (tty, O_RDWR | O_NONBLOCK);
+       if (serial->fd < 0) {
+               g_source_destroy(&serial->source);
+               return NULL;
+       }
+       tcgetattr(serial->fd, &termios);
+       serial->save_termios = termios;
+       cfmakeraw(&termios);
+       tcsetattr(serial->fd, TCSAFLUSH, &termios);
+
+       aoview_serial_printf(serial, "E 0\n");
+       tcdrain(serial->fd);
+       usleep(15*1000);
+       tcflush(serial->fd, TCIFLUSH);
+       serial->poll_fd.fd = serial->fd;
+       serial->poll_fd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
+       g_source_attach(&serial->source, NULL);
+       g_source_add_poll(&serial->source,&serial->poll_fd);
+       aoview_serial_set_callback(serial, NULL);
+       return serial;
+}
+
+void
+aoview_serial_close(struct aoview_serial *serial)
+{
+       g_source_remove_poll(&serial->source, &serial->poll_fd);
+       close(serial->fd);
+       g_source_destroy(&serial->source);
+}
+
+void
+aoview_serial_set_callback(struct aoview_serial *serial,
+                          aoview_serial_callback func)
+{
+       g_source_set_callback(&serial->source, (GSourceFunc) func, serial, NULL);
+}
diff --git a/ao-tools/ao-view/aoview_state.c b/ao-tools/ao-view/aoview_state.c
new file mode 100644 (file)
index 0000000..505bcdd
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+#include <math.h>
+
+static inline double sqr(double a) { return a * a; };
+
+static void
+aoview_great_circle (double start_lat, double start_lon,
+                    double end_lat, double end_lon,
+                    double *dist, double *bearing)
+{
+       const double rad = M_PI / 180;
+       const double earth_radius = 6371.2 * 1000;      /* in meters */
+       double lat1 = rad * start_lat;
+       double lon1 = rad * -start_lon;
+       double lat2 = rad * end_lat;
+       double lon2 = rad * -end_lon;
+
+       double d_lat = lat2 - lat1;
+       double d_lon = lon2 - lon1;
+
+       /* From http://en.wikipedia.org/wiki/Great-circle_distance */
+       double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) +
+                         sqr(cos(lat1) * sin(lat2) -
+                             sin(lat1) * cos(lat2) * cos(d_lon)));
+       double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon);
+       double d = atan2(vdn,vdd);
+       double course;
+
+       if (cos(lat1) < 1e-20) {
+               if (lat1 > 0)
+                       course = M_PI;
+               else
+                       course = -M_PI;
+       } else {
+               if (d < 1e-10)
+                       course = 0;
+               else
+                       course = acos((sin(lat2)-sin(lat1)*cos(d)) /
+                                     (sin(d)*cos(lat1)));
+               if (sin(lon2-lon1) > 0)
+                       course = 2 * M_PI-course;
+       }
+       *dist = d * earth_radius;
+       *bearing = course * 180/M_PI;
+}
+
+static void
+aoview_state_add_deg(int column, char *label, double deg, char pos, char neg)
+{
+       double  int_part;
+       double  min;
+       char    sign = pos;
+
+       if (deg < 0) {
+               deg = -deg;
+               sign = neg;
+       }
+       int_part = floor (deg);
+       min = (deg - int_part) * 60.0;
+       aoview_table_add_row(column, label, "%d°%lf'%c",
+                            (int) int_part, min, sign);
+
+}
+
+static char *ascent_states[] = {
+       "boost",
+       "fast",
+       "coast",
+       0,
+};
+
+static double
+aoview_time(void)
+{
+       struct timespec now;
+
+       clock_gettime(CLOCK_MONOTONIC, &now);
+       return (double) now.tv_sec + (double) now.tv_nsec / 1.0e9;
+}
+
+/*
+ * Fill out the derived data fields
+ */
+static void
+aoview_state_derive(struct cc_telem *data, struct aostate *state)
+{
+       int     i;
+       double  new_height;
+       double  height_change;
+       double  time_change;
+       double  accel_counts_per_mss;
+       int     tick_count;
+
+       state->report_time = aoview_time();
+
+       state->prev_data = state->data;
+       state->prev_npad = state->npad;
+       state->data = *data;
+       tick_count = data->tick;
+       if (tick_count < state->prev_data.tick)
+               tick_count += 65536;
+       time_change = (tick_count - state->prev_data.tick) / 100.0;
+
+       state->ground_altitude = aoview_pres_to_altitude(data->ground_pres);
+       new_height = aoview_pres_to_altitude(data->flight_pres) - state->ground_altitude;
+       height_change = new_height - state->height;
+       state->height = new_height;
+       if (time_change)
+               state->baro_speed = (state->baro_speed * 3 + (height_change / time_change)) / 4.0;
+       accel_counts_per_mss = ((data->accel_minus_g - data->accel_plus_g) / 2.0) / 9.80665;
+       state->acceleration = (data->ground_accel - data->flight_accel) / accel_counts_per_mss;
+       state->speed = data->flight_vel / (accel_counts_per_mss * 100.0);
+       state->temperature = cc_thermometer_to_temperature(data->temp);
+       state->drogue_sense = cc_ignitor_to_voltage(data->drogue);
+       state->main_sense = cc_ignitor_to_voltage(data->main);
+       state->battery = cc_battery_to_voltage(data->batt);
+       if (!strcmp(data->state, "pad")) {
+               if (data->gps.gps_locked && data->gps.nsat >= 4) {
+                       state->npad++;
+                       state->pad_lat_total += data->gps.lat;
+                       state->pad_lon_total += data->gps.lon;
+                       state->pad_alt_total += data->gps.alt;
+                       if (state->npad > 1) {
+                               state->pad_lat = (state->pad_lat * 31 + data->gps.lat) / 32.0;
+                               state->pad_lon = (state->pad_lon * 31 + data->gps.lon) / 32.0;
+                               state->pad_alt = (state->pad_alt * 31 + data->gps.alt) / 32.0;
+                       } else {
+                               state->pad_lat = data->gps.lat;
+                               state->pad_lon = data->gps.lon;
+                               state->pad_alt = data->gps.alt;
+                       }
+               }
+       }
+       state->ascent = FALSE;
+       for (i = 0; ascent_states[i]; i++)
+               if (!strcmp(data->state, ascent_states[i]))
+                       state->ascent = TRUE;
+
+       /* Only look at accelerometer data on the way up */
+       if (state->ascent && state->acceleration > state->max_acceleration)
+               state->max_acceleration = state->acceleration;
+       if (state->ascent && state->speed > state->max_speed)
+               state->max_speed = state->speed;
+
+       if (state->height > state->max_height)
+               state->max_height = state->height;
+       state->gps.gps_locked = data->gps.gps_locked;
+       state->gps.gps_connected = data->gps.gps_connected;
+       if (data->gps.gps_locked) {
+               state->gps = data->gps;
+               state->gps_valid = 1;
+               if (state->npad)
+                       aoview_great_circle(state->pad_lat, state->pad_lon, state->gps.lat, state->gps.lon,
+                                           &state->distance, &state->bearing);
+       }
+       if (data->gps_tracking.channels)
+               state->gps_tracking = data->gps_tracking;
+       if (state->npad) {
+               state->gps_height = state->gps.alt - state->pad_alt;
+       } else {
+               state->gps_height = 0;
+       }
+}
+
+void
+aoview_speak_state(struct aostate *state)
+{
+       if (strcmp(state->data.state, state->prev_data.state)) {
+               aoview_voice_speak("%s\n", state->data.state);
+               if (!strcmp(state->data.state, "drogue"))
+                       aoview_voice_speak("apogee %d meters\n",
+                                          (int) state->max_height);
+               if (!strcmp(state->prev_data.state, "boost"))
+                       aoview_voice_speak("max speed %d meters per second\n",
+                                          (int) state->max_speed);
+       }
+       if (state->prev_npad < MIN_PAD_SAMPLES && state->npad >= MIN_PAD_SAMPLES)
+               aoview_voice_speak("g p s ready\n");
+}
+
+void
+aoview_speak_height(struct aostate *state)
+{
+       aoview_voice_speak("%d meters\n", state->height);
+}
+
+struct aostate aostate;
+
+static guint aostate_timeout;
+
+#define COMPASS_LIMIT(n)       ((n * 22.5) + 22.5/2)
+
+static char *compass_points[] = {
+       "north",
+       "north north east",
+       "north east",
+       "east north east",
+       "east",
+       "east south east",
+       "south east",
+       "south south east",
+       "south",
+       "south south west",
+       "south west",
+       "west south west",
+       "west",
+       "west north west",
+       "north west",
+       "north north west",
+};
+
+static char *
+aoview_compass_point(double bearing)
+{
+       int     i;
+       while (bearing < 0)
+               bearing += 360.0;
+       while (bearing >= 360.0)
+               bearing -= 360.0;
+
+       i = floor ((bearing - 22.5/2) / 22.5 + 0.5);
+       if (i < 0) i = 0;
+       if (i >= sizeof (compass_points) / sizeof (compass_points[0]))
+               i = 0;
+       return compass_points[i];
+}
+
+static gboolean
+aoview_state_timeout(gpointer data)
+{
+       double  now = aoview_time();
+
+       if (strlen(aostate.data.state) > 0 && strcmp(aostate.data.state, "pad") != 0)
+               aoview_speak_height(&aostate);
+       if (now - aostate.report_time >= 20 || !strcmp(aostate.data.state, "landed")) {
+               if (!aostate.ascent) {
+                       if (fabs(aostate.baro_speed) < 20 && aostate.height < 100)
+                               aoview_voice_speak("rocket landed safely\n");
+                       else
+                               aoview_voice_speak("rocket may have crashed\n");
+                       if (aostate.gps_valid) {
+                               aoview_voice_speak("rocket reported %s of pad distance %d meters\n",
+                                                  aoview_compass_point(aostate.bearing),
+                                                  (int) aostate.distance);
+                       }
+               }
+               aostate_timeout = 0;
+               return FALSE;
+       }
+       return TRUE;
+}
+
+void
+aoview_state_reset(void)
+{
+       memset(&aostate, '\0', sizeof (aostate));
+}
+
+void
+aoview_state_notify(struct cc_telem *data)
+{
+       struct aostate *state = &aostate;
+       aoview_state_derive(data, state);
+       aoview_table_start();
+
+       if (state->npad >= MIN_PAD_SAMPLES)
+               aoview_table_add_row(0, "Ground state", "ready");
+       else
+               aoview_table_add_row(0, "Ground state", "waiting for gps (%d)",
+                                    MIN_PAD_SAMPLES - state->npad);
+       aoview_table_add_row(0, "Rocket state", "%s", state->data.state);
+       aoview_table_add_row(0, "Callsign", "%s", state->data.callsign);
+       aoview_table_add_row(0, "Rocket serial", "%d", state->data.serial);
+       aoview_table_add_row(0, "Rocket flight", "%d", state->data.flight);
+
+       aoview_table_add_row(0, "RSSI", "%6ddBm", state->data.rssi);
+       aoview_table_add_row(0, "Height", "%6dm", state->height);
+       aoview_table_add_row(0, "Max height", "%6dm", state->max_height);
+       aoview_table_add_row(0, "Acceleration", "%7.1fm/s²", state->acceleration);
+       aoview_table_add_row(0, "Max acceleration", "%7.1fm/s²", state->max_acceleration);
+       aoview_table_add_row(0, "Speed", "%7.1fm/s", state->ascent ? state->speed : state->baro_speed);
+       aoview_table_add_row(0, "Max Speed", "%7.1fm/s", state->max_speed);
+       aoview_table_add_row(0, "Temperature", "%6.2f°C", state->temperature);
+       aoview_table_add_row(0, "Battery", "%5.2fV", state->battery);
+       aoview_table_add_row(0, "Drogue", "%5.2fV", state->drogue_sense);
+       aoview_table_add_row(0, "Main", "%5.2fV", state->main_sense);
+       aoview_table_add_row(0, "Pad altitude", "%dm", state->ground_altitude);
+       aoview_table_add_row(1, "Satellites", "%d", state->gps.nsat);
+       if (state->gps.gps_locked) {
+               aoview_table_add_row(1, "GPS", "locked");
+       } else if (state->gps.gps_connected) {
+               aoview_table_add_row(1, "GPS", "unlocked");
+       } else {
+               aoview_table_add_row(1, "GPS", "not available");
+       }
+       if (state->gps_valid) {
+               aoview_state_add_deg(1, "Latitude", state->gps.lat, 'N', 'S');
+               aoview_state_add_deg(1, "Longitude", state->gps.lon, 'E', 'W');
+               aoview_table_add_row(1, "GPS altitude", "%d", state->gps.alt);
+               aoview_table_add_row(1, "GPS height", "%d", state->gps_height);
+               aoview_table_add_row(1, "GPS date", "%04d-%02d-%02d",
+                                    state->gps.gps_time.year,
+                                    state->gps.gps_time.month,
+                                    state->gps.gps_time.day);
+               aoview_table_add_row(1, "GPS time", "%02d:%02d:%02d",
+                                    state->gps.gps_time.hour,
+                                    state->gps.gps_time.minute,
+                                    state->gps.gps_time.second);
+       }
+       if (state->gps.gps_extended) {
+               aoview_table_add_row(1, "GPS ground speed", "%7.1fm/s %d°",
+                                    state->gps.ground_speed,
+                                    state->gps.course);
+               aoview_table_add_row(1, "GPS climb rate", "%7.1fm/s",
+                                    state->gps.climb_rate);
+               aoview_table_add_row(1, "GPS precision", "%4.1f(hdop) %3dm(h) %3dm(v)",
+                                    state->gps.hdop, state->gps.h_error, state->gps.v_error);
+       }
+       if (state->npad) {
+               aoview_table_add_row(1, "Distance from pad", "%5.0fm", state->distance);
+               aoview_table_add_row(1, "Direction from pad", "%4.0f°", state->bearing);
+               aoview_state_add_deg(1, "Pad latitude", state->pad_lat, 'N', 'S');
+               aoview_state_add_deg(1, "Pad longitude", state->pad_lon, 'E', 'W');
+               aoview_table_add_row(1, "Pad GPS alt", "%gm", state->pad_alt);
+       }
+       if (state->gps.gps_connected) {
+               int     nsat_vis = 0;
+               int     c;
+
+               aoview_table_add_row(2, "Satellites Visible", "%d", state->gps_tracking.channels);
+               for (c = 0; c < state->gps_tracking.channels; c++) {
+                       aoview_table_add_row(2, "Satellite id,C/N0",
+                                            "%3d,%2d",
+                                            state->gps_tracking.sats[c].svid,
+                                            state->gps_tracking.sats[c].c_n0);
+               }
+       }
+       aoview_table_finish();
+       aoview_label_show(state);
+       aoview_speak_state(state);
+       if (!aostate_timeout && strcmp(state->data.state, "pad") != 0)
+               aostate_timeout = g_timeout_add_seconds(10, aoview_state_timeout, NULL);
+}
+
+void
+aoview_state_new(void)
+{
+}
+
+void
+aoview_state_init(GladeXML *xml)
+{
+       aoview_state_new();
+}
diff --git a/ao-tools/ao-view/aoview_table.c b/ao-tools/ao-view/aoview_table.c
new file mode 100644 (file)
index 0000000..e75ae67
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+#define NCOL   3
+
+static GtkTreeView     *dataview[NCOL];
+static GtkListStore    *datalist[NCOL];
+
+void
+aoview_table_start(void)
+{
+       int col;
+       for (col = 0; col < NCOL; col++)
+               datalist[col] = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
+}
+
+void
+aoview_table_add_row(int col, char *label, char *format, ...)
+{
+       char            buf[1024];
+       va_list         ap;
+       GtkTreeIter     iter;
+
+       va_start(ap, format);
+       vsnprintf(buf, sizeof (buf), format, ap);
+       va_end(ap);
+       gtk_list_store_append(datalist[col], &iter);
+       gtk_list_store_set(datalist[col], &iter,
+                          0, label,
+                          1, buf,
+                          -1);
+}
+
+void
+aoview_table_finish(void)
+{
+       int     col;
+       for (col = 0; col < NCOL; col++) {
+               gtk_tree_view_set_model(dataview[col], GTK_TREE_MODEL(datalist[col]));
+               g_object_unref(G_OBJECT(datalist[col]));
+               gtk_tree_view_columns_autosize(dataview[col]);
+       }
+}
+
+void
+aoview_table_clear(void)
+{
+       int     col;
+       for (col = 0; col < NCOL; col++)
+               gtk_tree_view_set_model(dataview[col], NULL);
+}
+
+void
+aoview_table_init(GladeXML *xml)
+{
+       int     col;
+
+       for (col = 0; col < NCOL; col++) {
+               char    name[32];
+               sprintf(name, "dataview_%d", col);
+               dataview[col] = GTK_TREE_VIEW(glade_xml_get_widget(xml, name));
+               assert(dataview[col]);
+
+               aoview_add_plain_text_column(dataview[col], "Field", 0, 20);
+               aoview_add_plain_text_column(dataview[col], "Value", 1, 32);
+       }
+}
diff --git a/ao-tools/ao-view/aoview_util.c b/ao-tools/ao-view/aoview_util.c
new file mode 100644 (file)
index 0000000..6ea62ac
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+char *
+aoview_fullname (char *dir, char *file)
+{
+       char    *new;
+       int     dlen = strlen (dir);
+       int     flen = strlen (file);
+       int     slen = 0;
+
+       if (dir[dlen-1] != '/')
+               slen = 1;
+       new = malloc (dlen + slen + flen + 1);
+       if (!new)
+               return 0;
+       strcpy(new, dir);
+       if (slen)
+               strcat (new, "/");
+       strcat(new, file);
+       return new;
+}
+
+char *
+aoview_basename(char *file)
+{
+       char *b;
+
+       b = strrchr(file, '/');
+       if (!b)
+               return file;
+       return b + 1;
+}
+
+int
+aoview_mkdir(char *dir)
+{
+       char    *slash;
+       char    *d;
+       char    *part;
+
+       d = dir;
+       for (;;) {
+               slash = strchr (d, '/');
+               if (!slash)
+                       slash = d + strlen(d);
+               if (!*slash)
+                       break;
+               part = strndup(dir, slash - dir);
+               if (!access(part, F_OK))
+                       if (mkdir(part, 0777) < 0)
+                               return -errno;
+               free(part);
+               d = slash + 1;
+       }
+       return 0;
+}
+
+GtkTreeViewColumn *
+aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width)
+{
+       GtkCellRenderer *renderer;
+       GtkTreeViewColumn *column;
+
+       renderer = gtk_cell_renderer_text_new ();
+       g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
+       g_object_set(renderer, "width-chars", width, NULL);
+       column = gtk_tree_view_column_new_with_attributes (title, renderer,
+                                                          "text", model_column,
+                                                          NULL);
+       gtk_tree_view_column_set_resizable (column, FALSE);
+       gtk_tree_view_append_column (view, column);
+
+       return column;
+}
diff --git a/ao-tools/ao-view/aoview_voice.c b/ao-tools/ao-view/aoview_voice.c
new file mode 100644 (file)
index 0000000..24422df
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "aoview.h"
+
+#if HAVE_FLITE
+#include <stdarg.h>
+
+FILE   *aoview_flite;
+
+void aoview_voice_open(void)
+{
+       int     err;
+
+       if (!aoview_flite)
+               aoview_flite = aoview_flite_start();
+}
+
+void aoview_voice_close(void)
+{
+       if (aoview_flite) {
+               aoview_flite_stop();
+               aoview_flite = NULL;
+       }
+}
+
+void aoview_voice_speak(char *format, ...)
+{
+       va_list ap;
+
+       if (aoview_flite) {
+               va_start(ap, format);
+               vfprintf(aoview_flite, format, ap);
+               fflush(aoview_flite);
+               va_end(ap);
+       }
+}
+
+#else
+void aoview_voice_open(void)
+{
+}
+
+void aoview_voice_close(void)
+{
+}
+
+void aoview_voice_speak(char *format, ...)
+{
+}
+#endif
+
+
+static GtkCheckMenuItem        *voice_enable;
+
+#define ALTOS_VOICE_PATH       "/apps/aoview/voice"
+
+static void
+aoview_voice_enable(GtkWidget *widget, gpointer data)
+{
+       gboolean        enabled = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
+       GError          *error;
+       GConfClient     *gconf_client;
+
+       if (enabled) {
+               aoview_voice_open();
+               aoview_voice_speak("enable voice\n");
+       } else {
+               aoview_voice_speak("disable voice\n");
+               aoview_voice_close();
+       }
+       gconf_client = gconf_client_get_default();
+       gconf_client_set_bool(gconf_client,
+                             ALTOS_VOICE_PATH,
+                             enabled,
+                             &error);
+}
+
+void
+aoview_voice_init(GladeXML *xml)
+{
+       gboolean        enabled;
+       GConfClient     *gconf_client;
+
+       voice_enable = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(xml, "voice_enable"));
+       assert(voice_enable);
+
+       gconf_client = gconf_client_get_default();
+       enabled = TRUE;
+       if (gconf_client)
+       {
+               GError  *error;
+
+               error = NULL;
+               enabled = gconf_client_get_bool(gconf_client,
+                                               ALTOS_VOICE_PATH,
+                                               &error);
+               if (error)
+                       enabled = TRUE;
+       }
+       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(voice_enable), enabled);
+       if (enabled)
+               aoview_voice_open();
+
+       g_signal_connect(G_OBJECT(voice_enable), "toggled",
+                        G_CALLBACK(aoview_voice_enable),
+                        voice_enable);
+}
diff --git a/ao-tools/ao-view/design b/ao-tools/ao-view/design
new file mode 100644 (file)
index 0000000..6ec2ea7
--- /dev/null
@@ -0,0 +1,27 @@
+Requirements:
+       real-time display of telemetry
+       off-line display of logged data
+       Logging of telemetry
+       Capture of logged data to disk
+
+Input data:
+       accelerometer
+       barometer
+       thermometer
+       gps
+       drogue and main continuity
+       battery voltage
+       time
+       reported flight state
+       reported events
+
+Computed data:
+       velocity (from accelerometer)
+       altitude
+       range
+       direction
+
+Displays:
+       numeric display of current rocket status
+       (graphics come later)
+       text message log
index 9584e216f3c7b4290d05078d76f85aeab3580345..1f8f2e42f33eb7d1098bbcc0aab5dff1842acd74 100644 (file)
@@ -1,6 +1,9 @@
 noinst_LIBRARIES = libao-tools.a
 
-AM_CFLAGS=$(WARN_CFLAGS) $(LIBUSB_CFLAGS)
+AM_CFLAGS=$(WARN_CFLAGS) $(LIBUSB_CFLAGS) $(GNOME_CFLAGS)
+
+libao_tools_a_uneeded = \
+       cc-log.c
 
 libao_tools_a_SOURCES = \
        ccdbg-command.c \
@@ -14,9 +17,26 @@ libao_tools_a_SOURCES = \
        ccdbg-memory.c \
        ccdbg-rom.c \
        ccdbg-state.c \
+       cc-analyse.c \
+       cc-convert.c \
+       cc-dsp.c \
+       cc-integrate.c \
+       cc-period.c \
+       cc-process.c \
        cc-usb.c \
        cc-usb.h \
+       cc.h \
+       cc-usbdev.c \
+       cc-util.c \
        cc-bitbang.c \
        cc-bitbang.h \
+       cc-logfile.c \
+       cc-telem.c \
+       cc-telemetry.c \
+       cc-telemetry.h \
        cp-usb-async.c \
-       cp-usb-async.h
+       cp-usb-async.h \
+       i0.c \
+       chbevl.c \
+       mconf.h \
+       cephes.h
diff --git a/ao-tools/lib/cc-analyse.c b/ao-tools/lib/cc-analyse.c
new file mode 100644 (file)
index 0000000..27c416a
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <math.h>
+
+void
+cc_timedata_limits(struct cc_timedata *d, double min_time, double max_time, int *start, int *stop)
+{
+       int     i;
+
+       *start = -1;
+       for (i = 0; i < d->num; i++) {
+               if (*start < 0 && min_time <= d->data[i].time)
+                       *start = i;
+               if (d->data[i].time <= max_time)
+                       *stop = i;
+       }
+}
+
+int
+cc_timedata_min(struct cc_timedata *d, double min_time, double max_time)
+{
+       int     i;
+       int     set = 0;
+       int     min_i = -1;
+       double  min;
+
+       if (d->num == 0)
+               return -1;
+       for (i = 0; i < d->num; i++)
+               if (min_time <= d->data[i].time && d->data[i].time <= max_time)
+                       if (!set || d->data[i].value < min) {
+                               min_i = i;
+                               min = d->data[i].value;
+                               set = 1;
+                       }
+       return min_i;
+}
+
+int
+cc_timedata_min_mag(struct cc_timedata *d, double min_time, double max_time)
+{
+       int     i;
+       int     set = 0;
+       int     min_i = -1;
+       double  min;
+
+       if (d->num == 0)
+               return -1;
+       for (i = 0; i < d->num; i++)
+               if (min_time <= d->data[i].time && d->data[i].time <= max_time)
+                       if (!set || fabs(d->data[i].value) < min) {
+                               min_i = i;
+                               min = fabs(d->data[i].value);
+                               set = 1;
+                       }
+       return min_i;
+}
+
+int
+cc_timedata_max(struct cc_timedata *d, double min_time, double max_time)
+{
+       int     i;
+       double  max;
+       int     max_i = -1;
+       int     set = 0;
+
+       if (d->num == 0)
+               return -1;
+       for (i = 0; i < d->num; i++)
+               if (min_time <= d->data[i].time && d->data[i].time <= max_time)
+                       if (!set || d->data[i].value > max) {
+                               max_i = i;
+                               max = d->data[i].value;
+                               set = 1;
+                       }
+       return max_i;
+}
+
+int
+cc_timedata_max_mag(struct cc_timedata *d, double min_time, double max_time)
+{
+       int     i;
+       double  max;
+       int     max_i = -1;
+       int     set = 0;
+
+       if (d->num == 0)
+               return -1;
+       for (i = 0; i < d->num; i++)
+               if (min_time <= d->data[i].time && d->data[i].time <= max_time)
+                       if (!set || fabs(d->data[i].value) > max) {
+                               max_i = i;
+                               max = fabs(d->data[i].value);
+                               set = 1;
+                       }
+       return max_i;
+}
+
+double
+cc_timedata_average(struct cc_timedata *td, double start_time, double stop_time)
+{
+       int                     i;
+       double                  prev_time;
+       double                  next_time;
+       double                  interval;
+       double                  sum = 0.0;
+       double                  period = 0.0;
+
+       prev_time = start_time;
+       for (i = 0; i < td->num; i++) {
+               if (start_time <= td->data[i].time && td->data[i].time <= stop_time) {
+                       if (i < td->num - 1 && td->data[i+1].time < stop_time)
+                               next_time = (td->data[i].time + td->data[i+1].time) / 2.0;
+                       else
+                               next_time = stop_time;
+                       interval = next_time - prev_time;
+                       sum += td->data[i].value * interval;
+                       period += interval;
+                       prev_time = next_time;
+               }
+       }
+       return sum / period;
+}
+
+int
+cc_perioddata_limits(struct cc_perioddata *d, double min_time, double max_time, int *start, int *stop)
+{
+       double  start_d, stop_d;
+
+       if (d->num == 0)
+               return 0;
+       start_d = ceil((min_time - d->start) / d->step);
+       if (start_d < 0)
+               start_d = 0;
+       stop_d = floor((max_time - d->start) / d->step);
+       if (stop_d >= d->num)
+               stop_d = d->num - 1;
+       if (stop_d < start_d)
+               return 0;
+       *start = (int) start_d;
+       *stop = (int) stop_d;
+       return 1;
+}
+
+int
+cc_perioddata_min(struct cc_perioddata *d, double min_time, double max_time)
+{
+       int     i;
+       double  min;
+       int     min_i;
+       int     start, stop;
+
+       if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop))
+               return -1;
+       min = d->data[start];
+       min_i = start;
+       for (i = start + 1; i <= stop; i++)
+               if (d->data[i] < min) {
+                       min = d->data[i];
+                       min_i = i;
+               }
+       return min_i;
+}
+
+int
+cc_perioddata_min_mag(struct cc_perioddata *d, double min_time, double max_time)
+{
+       int     start, stop;
+       int     i;
+       double  min;
+       int     min_i;
+
+       if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop))
+               return -1;
+       min = d->data[start];
+       min_i = start;
+       for (i = start + 1; i <= stop; i++)
+               if (fabs(d->data[i]) < min) {
+                       min = fabs(d->data[i]);
+                       min_i = i;
+               }
+       return min_i;
+}
+
+int
+cc_perioddata_max(struct cc_perioddata *d, double min_time, double max_time)
+{
+       int     start, stop;
+       int     i;
+       double  max;
+       int     max_i;
+
+       if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop))
+               return -1;
+       max = d->data[start];
+       max_i = start;
+       for (i = start + 1; i <= stop; i++)
+               if (d->data[i] > max) {
+                       max = d->data[i];
+                       max_i = i;
+               }
+       return max_i;
+}
+
+int
+cc_perioddata_max_mag(struct cc_perioddata *d, double min_time, double max_time)
+{
+       int     start, stop;
+       int     i;
+       double  max;
+       int     max_i;
+
+       if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop))
+               return -1;
+       max = d->data[start];
+       max_i = start;
+       for (i = start + 1; i <= stop; i++)
+               if (fabs(d->data[i]) > max) {
+                       max = fabs(d->data[i]);
+                       max_i = i;
+               }
+       return max_i;
+}
+
+double
+cc_perioddata_average(struct cc_perioddata *d, double min_time, double max_time)
+{
+       int     start, stop;
+       int     i;
+       double  sum = 0.0;
+
+       if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop))
+               return 0.0;
+       for (i = start; i <= stop; i++)
+               sum += d->data[i];
+       return sum / (stop - start + 1);
+}
+
+double
+cc_perioddata_average_mag(struct cc_perioddata *d, double min_time, double max_time)
+{
+       int     start, stop;
+       int     i;
+       double  sum = 0.0;
+
+       if (!cc_perioddata_limits(d, min_time, max_time, &start, &stop))
+               return 0.0;
+       for (i = start; i <= stop; i++)
+               sum += fabs(d->data[i]);
+       return sum / (stop - start + 1);
+}
diff --git a/ao-tools/lib/cc-convert.c b/ao-tools/lib/cc-convert.c
new file mode 100644 (file)
index 0000000..8d6876a
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <math.h>
+
+/*
+ * Pressure Sensor Model, version 1.1
+ *
+ * written by Holly Grimes
+ *
+ * Uses the International Standard Atmosphere as described in
+ *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
+ *    from the Portland State Aerospace Society, except that the atmosphere
+ *    is divided into layers with each layer having a different lapse rate.
+ *
+ * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
+ *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
+ *
+ * Height measurements use the local tangent plane.  The postive z-direction is up.
+ *
+ * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
+ *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
+ *   in Joules/(kilogram-Kelvin).
+ */
+
+#define GRAVITATIONAL_ACCELERATION -9.80665
+#define AIR_GAS_CONSTANT       287.053
+#define NUMBER_OF_LAYERS       7
+#define MAXIMUM_ALTITUDE       84852.0
+#define MINIMUM_PRESSURE       0.3734
+#define LAYER0_BASE_TEMPERATURE        288.15
+#define LAYER0_BASE_PRESSURE   101325
+
+/* lapse rate and base altitude for each layer in the atmosphere */
+static const double lapse_rate[NUMBER_OF_LAYERS] = {
+       -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
+};
+
+static const int base_altitude[NUMBER_OF_LAYERS] = {
+       0, 11000, 20000, 32000, 47000, 51000, 71000
+};
+
+/* outputs atmospheric pressure associated with the given altitude. altitudes
+   are measured with respect to the mean sea level */
+double
+cc_altitude_to_pressure(double altitude)
+{
+
+   double base_temperature = LAYER0_BASE_TEMPERATURE;
+   double base_pressure = LAYER0_BASE_PRESSURE;
+
+   double pressure;
+   double base; /* base for function to determine pressure */
+   double exponent; /* exponent for function to determine pressure */
+   int layer_number; /* identifies layer in the atmosphere */
+   int delta_z; /* difference between two altitudes */
+
+   if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
+      return 0;
+
+   /* calculate the base temperature and pressure for the atmospheric layer
+      associated with the inputted altitude */
+   for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
+      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+      if (lapse_rate[layer_number] == 0.0) {
+         exponent = GRAVITATIONAL_ACCELERATION * delta_z
+              / AIR_GAS_CONSTANT / base_temperature;
+         base_pressure *= exp(exponent);
+      }
+      else {
+         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+         exponent = GRAVITATIONAL_ACCELERATION /
+              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+         base_pressure *= pow(base, exponent);
+      }
+      base_temperature += delta_z * lapse_rate[layer_number];
+   }
+
+   /* calculate the pressure at the inputted altitude */
+   delta_z = altitude - base_altitude[layer_number];
+   if (lapse_rate[layer_number] == 0.0) {
+      exponent = GRAVITATIONAL_ACCELERATION * delta_z
+           / AIR_GAS_CONSTANT / base_temperature;
+      pressure = base_pressure * exp(exponent);
+   }
+   else {
+      base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+      exponent = GRAVITATIONAL_ACCELERATION /
+           (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+      pressure = base_pressure * pow(base, exponent);
+   }
+
+   return pressure;
+}
+
+
+/* outputs the altitude associated with the given pressure. the altitude
+   returned is measured with respect to the mean sea level */
+double
+cc_pressure_to_altitude(double pressure)
+{
+
+   double next_base_temperature = LAYER0_BASE_TEMPERATURE;
+   double next_base_pressure = LAYER0_BASE_PRESSURE;
+
+   double altitude;
+   double base_pressure;
+   double base_temperature;
+   double base; /* base for function to determine base pressure of next layer */
+   double exponent; /* exponent for function to determine base pressure
+                             of next layer */
+   double coefficient;
+   int layer_number; /* identifies layer in the atmosphere */
+   int delta_z; /* difference between two altitudes */
+
+   if (pressure < 0)  /* illegal pressure */
+      return -1;
+   if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
+      return MAXIMUM_ALTITUDE;
+
+   /* calculate the base temperature and pressure for the atmospheric layer
+      associated with the inputted pressure. */
+   layer_number = -1;
+   do {
+      layer_number++;
+      base_pressure = next_base_pressure;
+      base_temperature = next_base_temperature;
+      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+      if (lapse_rate[layer_number] == 0.0) {
+         exponent = GRAVITATIONAL_ACCELERATION * delta_z
+              / AIR_GAS_CONSTANT / base_temperature;
+         next_base_pressure *= exp(exponent);
+      }
+      else {
+         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+         exponent = GRAVITATIONAL_ACCELERATION /
+              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+         next_base_pressure *= pow(base, exponent);
+      }
+      next_base_temperature += delta_z * lapse_rate[layer_number];
+   }
+   while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
+
+   /* calculate the altitude associated with the inputted pressure */
+   if (lapse_rate[layer_number] == 0.0) {
+      coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
+                                                    * base_temperature;
+      altitude = base_altitude[layer_number]
+                    + coefficient * log(pressure / base_pressure);
+   }
+   else {
+      base = pressure / base_pressure;
+      exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
+                                       / GRAVITATIONAL_ACCELERATION;
+      coefficient = base_temperature / lapse_rate[layer_number];
+      altitude = base_altitude[layer_number]
+                      + coefficient * (pow(base, exponent) - 1);
+   }
+
+   return altitude;
+}
+
+/*
+ * Values for our MP3H6115A pressure sensor
+ *
+ * From the data sheet:
+ *
+ * Pressure range: 15-115 kPa
+ * Voltage at 115kPa: 2.82
+ * Output scale: 27mV/kPa
+ *
+ *
+ * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa
+ * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa
+ */
+
+static const double counts_per_kPa = 27 * 2047 / 3300;
+static const double counts_at_101_3kPa = 1674.0;
+
+double
+cc_barometer_to_pressure(double count)
+{
+       return ((count / 16.0) / 2047.0 + 0.095) / 0.009 * 1000.0;
+}
+
+double
+cc_barometer_to_altitude(double baro)
+{
+       double Pa = cc_barometer_to_pressure(baro);
+       return cc_pressure_to_altitude(Pa);
+}
+
+static const double count_per_mss = 27.0;
+
+double
+cc_accelerometer_to_acceleration(double accel, double ground_accel)
+{
+       return (ground_accel - accel) / count_per_mss;
+}
+
+/* Value for the CC1111 built-in temperature sensor
+ * Output voltage at 0°C = 0.755V
+ * Coefficient = 0.00247V/°C
+ * Reference voltage = 1.25V
+ *
+ * temp = ((value / 32767) * 1.25 - 0.755) / 0.00247
+ *      = (value - 19791.268) / 32768 * 1.25 / 0.00247
+ */
+
+double
+cc_thermometer_to_temperature(double thermo)
+{
+       return (thermo - 19791.268) / 32728.0 * 1.25 / 0.00247;
+}
+
+double
+cc_battery_to_voltage(double battery)
+{
+       return battery / 32767.0 * 5.0;
+}
+
+double
+cc_ignitor_to_voltage(double ignite)
+{
+       return ignite / 32767 * 15.0;
+}
+
+static inline double sqr(double a) { return a * a; }
+
+void
+cc_great_circle (double start_lat, double start_lon,
+                double end_lat, double end_lon,
+                double *dist, double *bearing)
+{
+       const double rad = M_PI / 180;
+       const double earth_radius = 6371.2 * 1000;      /* in meters */
+       double lat1 = rad * start_lat;
+       double lon1 = rad * -start_lon;
+       double lat2 = rad * end_lat;
+       double lon2 = rad * -end_lon;
+
+//     double d_lat = lat2 - lat1;
+       double d_lon = lon2 - lon1;
+
+       /* From http://en.wikipedia.org/wiki/Great-circle_distance */
+       double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) +
+                         sqr(cos(lat1) * sin(lat2) -
+                             sin(lat1) * cos(lat2) * cos(d_lon)));
+       double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon);
+       double d = atan2(vdn,vdd);
+       double course;
+
+       if (cos(lat1) < 1e-20) {
+               if (lat1 > 0)
+                       course = M_PI;
+               else
+                       course = -M_PI;
+       } else {
+               if (d < 1e-10)
+                       course = 0;
+               else
+                       course = acos((sin(lat2)-sin(lat1)*cos(d)) /
+                                     (sin(d)*cos(lat1)));
+               if (sin(lon2-lon1) > 0)
+                       course = 2 * M_PI-course;
+       }
+       *dist = d * earth_radius;
+       *bearing = course * 180/M_PI;
+}
diff --git a/ao-tools/lib/cc-dsp.c b/ao-tools/lib/cc-dsp.c
new file mode 100644 (file)
index 0000000..518c1a6
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include "cephes.h"
+#include <math.h>
+#include <stdlib.h>
+
+static inline double sqr (double x) { return x * x; }
+
+/*
+ * Kaiser Window digital filter
+ */
+
+#if 0
+/* not used in this program */
+static double highpass (double n, double m, double wc)
+{
+       double  alpha = m/2;
+       double  dist;
+
+       dist = n - alpha;
+       if (dist == 0)
+               return (M_PI/2 - wc) / M_PI;
+       return -sin(dist * (M_PI/2-wc)) / (M_PI * dist);
+}
+#endif
+
+static double lowpass (double n, double m, double wc)
+{
+       double  alpha = m/2;
+       double  dist;
+       dist = n - alpha;
+       if (dist == 0)
+               return wc / M_PI;
+       return sin (wc * dist) / (M_PI * dist);
+}
+
+static double kaiser (double n, double m, double beta)
+{
+       double alpha = m / 2;
+       return i0 (beta * sqrt (1 - sqr((n - alpha) / alpha))) / i0(beta);
+}
+
+static double beta (double A)
+{
+       if (A > 50)
+               return 0.1102 * (A - 8.7);
+       else if (A >= 21)
+               return 0.5842 * pow((A - 21), 0.4) + 0.07886 * (A - 21);
+       else
+               return 0.0;
+}
+
+static int M (double A, double delta_omega)
+{
+       if (A > 21)
+               return ceil ((A - 7.95) / (2.285 * delta_omega));
+       else
+               return ceil(5.79 / delta_omega);
+}
+
+struct filter_param {
+       double omega_pass;
+       double delta_omega;
+       double A;
+       double beta;
+       int M;
+} filter_param_t;
+
+static struct filter_param
+filter (double omega_pass, double omega_stop, double error)
+{
+       struct filter_param  p;
+       p.omega_pass = omega_pass;
+       p.delta_omega = omega_stop - omega_pass;
+       p.A = -20 * log10 (error);
+       p.beta = beta (p.A);
+       p.M = M (p.A, p.delta_omega);
+       if ((p.M & 1) == 1)
+               p.M++;
+       return p;
+}
+
+static double *
+make_low_pass_filter(double omega_pass, double omega_stop, double error, int *length_p)
+{
+       struct filter_param     p = filter(omega_pass, omega_stop, error);
+       int             length;
+       int             n;
+       double          *lpf;
+
+       length = p.M + 1;
+       lpf = calloc (length, sizeof(double));
+       for (n = 0; n < length; n++)
+               lpf[n] = lowpass(n, p.M, omega_pass) * kaiser(n, p.M, p.beta);
+       *length_p = length;
+       return lpf;
+}
+
+static double *
+convolve(double *d, int d_len, double *e, int e_len)
+{
+       int w = (e_len - 1) / 2;
+       int n;
+       double *con = calloc (d_len, sizeof (double));
+
+       for (n = 0; n < d_len; n++) {
+               double  v = 0;
+               int o;
+               for (o = -w; o <= w; o++) {
+                       int     p = n + o;
+                       double sample = p < 0 ? d[0] : p >= d_len ? d[d_len-1] : d[p];
+                       v += sample * e[o + w];
+               }
+               con[n] = v;
+       }
+       return con;
+}
+
+double *
+cc_low_pass(double *data, int data_len, double omega_pass, double omega_stop, double error)
+{
+       int fir_len;
+       double *fir = make_low_pass_filter(omega_pass, omega_stop, error, &fir_len);
+       double *result;
+
+       result = convolve(data, data_len, fir, fir_len);
+       free(fir);
+       return result;
+}
diff --git a/ao-tools/lib/cc-integrate.c b/ao-tools/lib/cc-integrate.c
new file mode 100644 (file)
index 0000000..ba50761
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <stdlib.h>
+
+struct cc_timedata *
+cc_timedata_convert(struct cc_timedata *d, double (*f)(double v, double a), double a)
+{
+       struct cc_timedata      *r;
+       int                     n;
+
+       r = calloc (1, sizeof (struct cc_timedata));
+       r->num = d->num;
+       r->size = d->num;
+       r->data = calloc (r->size, sizeof (struct cc_timedataelt));
+       r->time_offset = d->time_offset;
+       for (n = 0; n < d->num; n++) {
+               r->data[n].time = d->data[n].time;
+               r->data[n].value = f(d->data[n].value, a);
+       }
+       return r;
+}
+
+struct cc_timedata *
+cc_timedata_integrate(struct cc_timedata *d, double min_time, double max_time)
+{
+       struct cc_timedata      *i;
+       int                     n, m;
+       int                     start, stop;
+
+       cc_timedata_limits(d, min_time, max_time, &start, &stop);
+       i = calloc (1, sizeof (struct cc_timedata));
+       i->num = stop - start + 1;
+       i->size = i->num;
+       i->data = calloc (i->size, sizeof (struct cc_timedataelt));
+       i->time_offset = d->data[start].time;
+       for (n = 0; n < i->num; n++) {
+               m = n + start;
+               i->data[n].time = d->data[m].time;
+               if (n == 0) {
+                       i->data[n].value = 0;
+               } else {
+                       i->data[n].value = i->data[n-1].value +
+                               (d->data[m].value + d->data[m-1].value) / 2 *
+                               ((d->data[m].time - d->data[m-1].time) / 100.0);
+               }
+       }
+       return i;
+}
+
+struct cc_perioddata *
+cc_perioddata_differentiate(struct cc_perioddata *i)
+{
+       struct cc_perioddata    *d;
+       int                     n;
+
+       d = calloc (1, sizeof (struct cc_perioddata));
+       d->num = i->num;
+       d->start = i->start;
+       d->step = i->step;
+       d->data = calloc (d->num, sizeof(double));
+       for (n = 1; n < d->num; n++)
+               d->data[n] = (i->data[n] - i->data[n-1]) / (i->step / 100.0);
+       d->data[0] = d->data[1];
+       return d;
+}
diff --git a/ao-tools/lib/cc-log.c b/ao-tools/lib/cc-log.c
new file mode 100644 (file)
index 0000000..ed51f87
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <gconf/gconf-client.h>
+#include "cc.h"
+
+static char *cc_file_dir;
+
+#define ALTOS_DIR_PATH "/apps/aoview/log_dir"
+#define DEFAULT_DIR    "AltOS"
+
+static void
+cc_file_save_conf(void)
+{
+       GConfClient     *gconf_client;
+
+       g_type_init();
+       gconf_client = gconf_client_get_default();
+       if (gconf_client)
+       {
+               gconf_client_set_string(gconf_client,
+                                       ALTOS_DIR_PATH,
+                                       cc_file_dir,
+                                       NULL);
+               g_object_unref(G_OBJECT(gconf_client));
+       }
+}
+
+static void
+cc_file_load_conf(void)
+{
+       char *file_dir;
+       GConfClient     *gconf_client;
+
+       g_type_init();
+       gconf_client = gconf_client_get_default();
+       if (gconf_client)
+       {
+               file_dir = gconf_client_get_string(gconf_client,
+                                                  ALTOS_DIR_PATH,
+                                                  NULL);
+               g_object_unref(G_OBJECT(gconf_client));
+               if (file_dir)
+                       cc_file_dir = strdup(file_dir);
+       }
+}
+
+void
+cc_set_log_dir(char *dir)
+{
+       cc_file_dir = strdup(dir);
+       cc_file_save_conf();
+}
+
+char *
+cc_get_log_dir(void)
+{
+       cc_file_load_conf();
+       if (!cc_file_dir) {
+               cc_file_dir = cc_fullname(getenv("HOME"), DEFAULT_DIR);
+               cc_file_save_conf();
+       }
+       return cc_file_dir;
+}
+
+char *
+cc_make_filename(int serial, int flight, char *ext)
+{
+       char            base[50];
+       char            seq[20];
+       struct tm       tm;
+       time_t          now;
+       char            *full;
+       int             r;
+       int             sequence;
+
+       now = time(NULL);
+       (void) localtime_r(&now, &tm);
+       cc_mkdir(cc_get_log_dir());
+       sequence = 0;
+       for (;;) {
+               if (sequence)
+                       snprintf(seq, sizeof(seq), "-seq-%03d", sequence);
+               else
+                       seq[0] = '\0';
+
+               snprintf(base, sizeof (base), "%04d-%02d-%02d-serial-%03d-flight-%03d%s.%s",
+                        tm.tm_year + 1900,
+                        tm.tm_mon + 1,
+                        tm.tm_mday,
+                        serial,
+                        flight,
+                        seq,
+                        ext);
+               full = cc_fullname(cc_get_log_dir(), base);
+               r = access(full, F_OK);
+               if (r < 0)
+                       return full;
+               free(full);
+               sequence++;
+       }
+
+}
diff --git a/ao-tools/lib/cc-logfile.c b/ao-tools/lib/cc-logfile.c
new file mode 100644 (file)
index 0000000..842e5c7
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int
+timedata_add(struct cc_timedata *data, double time, double value)
+{
+       struct cc_timedataelt   *newdata;
+       int                     newsize;
+       if (data->size == data->num) {
+               if (data->size == 0)
+                       newdata = malloc((newsize = 256) * sizeof (struct cc_timedataelt));
+               else
+                       newdata = realloc (data->data, (newsize = data->size * 2)
+                                          * sizeof (struct cc_timedataelt));
+               if (!newdata)
+                       return 0;
+               data->size = newsize;
+               data->data = newdata;
+       }
+       time += data->time_offset;
+       if (data->num && data->data[data->num-1].time > time) {
+               data->time_offset += 65536;
+               time += 65536;
+       }
+       data->data[data->num].time = time;
+       data->data[data->num].value = value;
+       data->num++;
+       return 1;
+}
+
+static void
+timedata_free(struct cc_timedata *data)
+{
+       if (data->data)
+               free(data->data);
+}
+
+static int
+gpsdata_add(struct cc_gpsdata *data, struct cc_gpselt *elt)
+{
+       struct cc_gpselt        *newdata;
+       int                     newsize;
+       if (data->size == data->num) {
+               if (data->size == 0)
+                       newdata = malloc((newsize = 256) * sizeof (struct cc_gpselt));
+               else
+                       newdata = realloc (data->data, (newsize = data->size * 2)
+                                          * sizeof (struct cc_gpselt));
+               if (!newdata)
+                       return 0;
+               data->size = newsize;
+               data->data = newdata;
+       }
+       elt->time += data->time_offset;
+       if (data->num && data->data[data->num-1].time > elt->time) {
+               data->time_offset += 65536;
+               elt->time += 65536;
+       }
+       data->data[data->num] = *elt;
+       data->num++;
+       return 1;
+}
+
+static int
+gpssat_add(struct cc_gpsdata *data, struct cc_gpssat *sat)
+{
+       int     i, j;
+       int     reuse = 0;
+       int     newsizesats;
+       struct cc_gpssats       *newsats;
+
+       for (i = data->numsats; --i >= 0;) {
+               if (data->sats[i].sat[0].time == sat->time) {
+                       reuse = 1;
+                       break;
+               }
+               if (data->sats[i].sat[0].time < sat->time)
+                       break;
+       }
+       if (!reuse) {
+               if (data->numsats == data->sizesats) {
+                       if (data->sizesats == 0)
+                               newsats = malloc((newsizesats = 256) * sizeof (struct cc_gpssats));
+                       else
+                               newsats = realloc (data->sats, (newsizesats = data->sizesats * 2)
+                                                  * sizeof (struct cc_gpssats));
+                       if (!newsats)
+                               return 0;
+                       data->sats = newsats;
+                       data->sizesats = newsizesats;
+               }
+               i = data->numsats++;
+               data->sats[i].nsat = 0;
+       }
+       j = data->sats[i].nsat;
+       if (j < 12) {
+               data->sats[i].sat[j] = *sat;
+               data->sats[i].nsat = j + 1;
+       }
+       return 1;
+}
+
+static void
+gpsdata_free(struct cc_gpsdata *data)
+{
+       if (data->data)
+               free(data->data);
+}
+
+#define AO_LOG_FLIGHT          'F'
+#define AO_LOG_SENSOR          'A'
+#define AO_LOG_TEMP_VOLT       'T'
+#define AO_LOG_DEPLOY          'D'
+#define AO_LOG_STATE           'S'
+#define AO_LOG_GPS_TIME                'G'
+#define AO_LOG_GPS_LAT         'N'
+#define AO_LOG_GPS_LON         'W'
+#define AO_LOG_GPS_ALT         'H'
+#define AO_LOG_GPS_SAT         'V'
+#define AO_LOG_GPS_DATE                'Y'
+
+#define AO_LOG_POS_NONE                (~0UL)
+
+#define GPS_TIME       1
+#define GPS_LAT                2
+#define GPS_LON                4
+#define GPS_ALT                8
+
+static int
+read_eeprom(const char *line, struct cc_flightraw *f, double *ground_pres, int *ground_pres_count)
+{
+       char    type;
+       int     tick;
+       int     a, b;
+       static struct cc_gpselt gps;
+       static int              gps_valid;
+       struct cc_gpssat        sat;
+       int     serial;
+
+       if (sscanf(line, "serial-number %u", &serial) == 1) {
+               f->serial = serial;
+               return 1;
+       }
+       if (sscanf(line, "%c %x %x %x", &type, &tick, &a, &b) != 4)
+               return 0;
+       switch (type) {
+       case AO_LOG_FLIGHT:
+               f->ground_accel = a;
+               f->ground_pres = 0;
+               f->flight = b;
+               *ground_pres = 0;
+               *ground_pres_count = 0;
+               break;
+       case AO_LOG_SENSOR:
+               timedata_add(&f->accel, tick, a);
+               timedata_add(&f->pres, tick, b);
+               if (*ground_pres_count < 20) {
+                       *ground_pres += b;
+                       (*ground_pres_count)++;
+                       if (*ground_pres_count >= 20)
+                               f->ground_pres = *ground_pres / *ground_pres_count;
+               }
+               break;
+       case AO_LOG_TEMP_VOLT:
+               timedata_add(&f->temp, tick, a);
+               timedata_add(&f->volt, tick, b);
+               break;
+       case AO_LOG_DEPLOY:
+               timedata_add(&f->drogue, tick, a);
+               timedata_add(&f->main, tick, b);
+               break;
+       case AO_LOG_STATE:
+               timedata_add(&f->state, tick, a);
+               break;
+       case AO_LOG_GPS_TIME:
+               /* the flight computer writes TIME first, so reset
+                * any stale data before adding this record
+                */
+               gps.time = tick;
+               gps.hour = (a & 0xff);
+               gps.minute = (a >> 8) & 0xff;
+               gps.second = (b & 0xff);
+               gps.flags = (b >> 8) & 0xff;
+               gps_valid = GPS_TIME;
+               break;
+       case AO_LOG_GPS_LAT:
+               gps.lat = ((int32_t) (a + (b << 16))) / 10000000.0;
+               gps_valid |= GPS_LAT;
+               break;
+       case AO_LOG_GPS_LON:
+               gps.lon = ((int32_t) (a + (b << 16))) / 10000000.0;
+               gps_valid |= GPS_LON;
+               break;
+       case AO_LOG_GPS_ALT:
+               gps.alt = (int16_t) a;
+               gps_valid |= GPS_ALT;
+               break;
+       case AO_LOG_GPS_SAT:
+               sat.time = tick;
+               sat.svid = a;
+               sat.c_n = (b >> 8) & 0xff;
+               gpssat_add(&f->gps, &sat);
+               break;
+       case AO_LOG_GPS_DATE:
+               f->year = 2000 + (a & 0xff);
+               f->month = (a >> 8) & 0xff;
+               f->day = (b & 0xff);
+               break;
+       default:
+               return 0;
+       }
+       if (gps_valid == 0xf) {
+               gps_valid = 0;
+               gpsdata_add(&f->gps, &gps);
+       }
+       return 1;
+}
+
+static const char *state_names[] = {
+       "startup",
+       "idle",
+       "pad",
+       "boost",
+       "fast",
+       "coast",
+       "drogue",
+       "main",
+       "landed",
+       "invalid"
+};
+
+static enum ao_flight_state
+state_name_to_state(char *state_name)
+{
+       enum ao_flight_state    state;
+       for (state = ao_flight_startup; state < ao_flight_invalid; state++)
+               if (!strcmp(state_names[state], state_name))
+                       return state;
+       return ao_flight_invalid;
+}
+
+static int
+read_telem(const char *line, struct cc_flightraw *f)
+{
+       struct cc_telem         telem;
+       struct cc_gpselt        gps;
+       struct cc_gpssat        sat;
+       int                     s;
+
+       if (!cc_telem_parse(line, &telem))
+               return 0;
+       f->ground_accel = telem.ground_accel;
+       f->ground_pres = telem.ground_pres;
+       f->flight = 0;
+       timedata_add(&f->accel, telem.tick, telem.flight_accel);
+       timedata_add(&f->pres, telem.tick, telem.flight_pres);
+       timedata_add(&f->temp, telem.tick, telem.temp);
+       timedata_add(&f->volt, telem.tick, telem.batt);
+       timedata_add(&f->drogue, telem.tick, telem.drogue);
+       timedata_add(&f->main, telem.tick, telem.main);
+       timedata_add(&f->state, telem.tick, state_name_to_state(telem.state));
+       if (telem.gps.gps_locked) {
+               f->year = telem.gps.gps_time.year;
+               f->month = telem.gps.gps_time.month;
+               f->day = telem.gps.gps_time.day;
+               gps.time = telem.tick;
+               gps.lat = telem.gps.lat;
+               gps.lon = telem.gps.lon;
+               gps.alt = telem.gps.alt;
+               gps.hour = telem.gps.gps_time.hour;
+               gps.minute = telem.gps.gps_time.minute;
+               gps.second = telem.gps.gps_time.second;
+               gpsdata_add(&f->gps, &gps);
+       }
+       for (s = 0; s < telem.gps_tracking.channels; s++) {
+               sat.time = telem.tick;
+               sat.svid = telem.gps_tracking.sats[s].svid;
+               sat.c_n = telem.gps_tracking.sats[s].c_n0;
+               gpssat_add(&f->gps, &sat);
+       }
+       return 1;
+}
+
+struct cc_flightraw *
+cc_log_read(FILE *file)
+{
+       struct cc_flightraw     *f;
+       char                    line[8192];
+       double                  ground_pres;
+       int                     ground_pres_count;
+
+       f = calloc(1, sizeof (struct cc_flightraw));
+       if (!f)
+               return NULL;
+       while (fgets(line, sizeof (line), file)) {
+               if (read_eeprom(line, f, &ground_pres, &ground_pres_count))
+                       continue;
+               if (read_telem(line, f))
+                       continue;
+               fprintf (stderr, "invalid line: %s", line);
+       }
+       return f;
+}
+
+void
+cc_flightraw_free(struct cc_flightraw *raw)
+{
+       timedata_free(&raw->accel);
+       timedata_free(&raw->pres);
+       timedata_free(&raw->temp);
+       timedata_free(&raw->volt);
+       timedata_free(&raw->main);
+       timedata_free(&raw->drogue);
+       timedata_free(&raw->state);
+       gpsdata_free(&raw->gps);
+       free(raw);
+}
diff --git a/ao-tools/lib/cc-period.c b/ao-tools/lib/cc-period.c
new file mode 100644 (file)
index 0000000..844ac79
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <stdlib.h>
+#include <math.h>
+
+struct cc_perioddata *
+cc_period_make(struct cc_timedata *td, double start_time, double stop_time)
+{
+       int                     len = stop_time - start_time + 1;
+       struct cc_perioddata    *pd;
+       int                     i, j;
+       double                  t;
+
+       pd = calloc(1, sizeof (struct cc_perioddata));
+       pd->start = start_time;
+       pd->step = 1;
+       pd->num = len;
+       pd->data = calloc(len, sizeof(double));
+       j = 0;
+       for (i = 0; i < pd->num; i++) {
+               t = start_time + i * pd->step;
+               while (j < td->num - 1 && fabs(t - td->data[j].time) >= fabs(t - td->data[j+1].time))
+                       j++;
+               pd->data[i] = td->data[j].value;
+       }
+       return pd;
+}
+
+struct cc_perioddata *
+cc_period_low_pass(struct cc_perioddata *raw, double omega_pass, double omega_stop, double error)
+{
+       struct cc_perioddata *filtered;
+
+       filtered = calloc (1, sizeof (struct cc_perioddata));
+       filtered->start = raw->start;
+       filtered->step = raw->step;
+       filtered->num = raw->num;
+       filtered->data = cc_low_pass(raw->data, raw->num, omega_pass, omega_stop, error);
+       return filtered;
+}
diff --git a/ao-tools/lib/cc-process.c b/ao-tools/lib/cc-process.c
new file mode 100644 (file)
index 0000000..c756b57
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+static void
+cook_timed(struct cc_timedata *td, struct cc_perioddata *pd,
+          double start_time, double stop_time,
+          double omega_pass, double omega_stop, double error)
+{
+       struct cc_perioddata    *unfiltered, *filtered;
+
+       unfiltered = cc_period_make(td, start_time, stop_time);
+       filtered = cc_period_low_pass (unfiltered, omega_pass, omega_stop, error);
+       *pd = *filtered;
+       free (filtered);
+       free (unfiltered->data);
+       free (unfiltered);
+}
+
+static double
+barometer_to_altitude(double b, double pad_alt)
+{
+       return cc_barometer_to_altitude(b) - pad_alt;
+}
+
+struct cc_flightcooked *
+cc_flight_cook(struct cc_flightraw *raw)
+{
+       struct cc_flightcooked *cooked;
+       double                  flight_start;
+       double                  flight_stop;
+       int                     start_set = 0;
+       int                     stop_set = 0;
+       int                     i;
+       struct cc_timedata      *accel;
+       struct cc_timedata      *accel_speed;
+       struct cc_timedata      *accel_pos;
+       struct cc_timedata      *pres;
+       struct cc_perioddata    *pres_speed;
+       struct cc_perioddata    *pres_accel;
+
+       if (raw->accel.num == 0)
+               return NULL;
+
+       cooked = calloc (1, sizeof (struct cc_flightcooked));
+
+       /*
+        * Find flight start and stop times by looking at
+        * state transitions. The stop time is set to the time
+        * of landing, which may be long after it landed (due to radio
+        * issues). Refine this value by looking through the sensor data
+        */
+       for (i = 0; i < raw->state.num; i++) {
+               if (!start_set && raw->state.data[i].value > ao_flight_pad) {
+                       flight_start = raw->state.data[i].time - 10;
+                       start_set = 1;
+               }
+               if (!stop_set && raw->state.data[i].value > ao_flight_main) {
+                       flight_stop = raw->state.data[i].time;
+                       stop_set = 1;
+               }
+       }
+
+       if (!start_set || flight_start < raw->accel.data[0].time)
+               flight_start = raw->accel.data[0].time;
+       if (stop_set) {
+               for (i = 0; i < raw->accel.num - 1; i++) {
+                       if (raw->accel.data[i+1].time >= flight_stop) {
+                               flight_stop = raw->accel.data[i].time;
+                               break;
+                       }
+               }
+       } else {
+               flight_stop = raw->accel.data[raw->accel.num-1].time;
+       }
+       cooked->flight_start = flight_start;
+       cooked->flight_stop = flight_stop;
+
+       /* Integrate the accelerometer data to get speed and position */
+       accel = cc_timedata_convert(&raw->accel, cc_accelerometer_to_acceleration, raw->ground_accel);
+       cooked->accel = *accel;
+       free(accel);
+       accel_speed = cc_timedata_integrate(&cooked->accel, flight_start - 10, flight_stop);
+       accel_pos = cc_timedata_integrate(accel_speed, flight_start - 10, flight_stop);
+
+#define ACCEL_OMEGA_PASS       (2 * M_PI * 20 / 100)
+#define ACCEL_OMEGA_STOP       (2 * M_PI * 30 / 100)
+#define BARO_OMEGA_PASS                (2 * M_PI * .5 / 100)
+#define BARO_OMEGA_STOP                (2 * M_PI * 1 / 100)
+#define FILTER_ERROR           (1e-8)
+
+       cook_timed(&cooked->accel, &cooked->accel_accel,
+                  flight_start, flight_stop,
+                  ACCEL_OMEGA_PASS, ACCEL_OMEGA_STOP, FILTER_ERROR);
+       cook_timed(accel_speed, &cooked->accel_speed,
+                  flight_start, flight_stop,
+                  ACCEL_OMEGA_PASS, ACCEL_OMEGA_STOP, FILTER_ERROR);
+       free(accel_speed->data); free(accel_speed);
+       cook_timed(accel_pos, &cooked->accel_pos,
+                  flight_start, flight_stop,
+                  ACCEL_OMEGA_PASS, ACCEL_OMEGA_STOP, FILTER_ERROR);
+       free(accel_pos->data); free(accel_pos);
+
+       /* Filter the pressure data */
+       pres = cc_timedata_convert(&raw->pres, barometer_to_altitude,
+                                  cc_barometer_to_altitude(raw->ground_pres));
+       cooked->pres = *pres;
+       free(pres);
+       cook_timed(&cooked->pres, &cooked->pres_pos,
+                  flight_start, flight_stop,
+                  BARO_OMEGA_PASS, BARO_OMEGA_STOP, FILTER_ERROR);
+       /* differentiate twice to get to acceleration */
+       pres_speed = cc_perioddata_differentiate(&cooked->pres_pos);
+       pres_accel = cc_perioddata_differentiate(pres_speed);
+
+       cooked->pres_speed = *pres_speed;
+       free(pres_speed);
+       cooked->pres_accel = *pres_accel;
+       free(pres_accel);
+
+       /* copy state */
+       cooked->state.num = raw->state.num;
+       cooked->state.size = raw->state.num;
+       cooked->state.data = calloc(cooked->state.num, sizeof (struct cc_timedataelt));
+       memcpy(cooked->state.data, raw->state.data, cooked->state.num * sizeof (struct cc_timedataelt));
+       cooked->state.time_offset = raw->state.time_offset;
+       return cooked;
+}
+
+#define if_free(x)     ((x) ? free(x) : (void) 0)
+
+void
+cc_flightcooked_free(struct cc_flightcooked *cooked)
+{
+       if_free(cooked->accel_accel.data);
+       if_free(cooked->accel_speed.data);
+       if_free(cooked->accel_pos.data);
+       if_free(cooked->pres_pos.data);
+       if_free(cooked->pres_speed.data);
+       if_free(cooked->pres_accel.data);
+       if_free(cooked->gps_lat.data);
+       if_free(cooked->gps_lon.data);
+       if_free(cooked->gps_alt.data);
+       if_free(cooked->state.data);
+       if_free(cooked->accel.data);
+       if_free(cooked->pres.data);
+       free(cooked);
+}
diff --git a/ao-tools/lib/cc-telem.c b/ao-tools/lib/cc-telem.c
new file mode 100644 (file)
index 0000000..aa52b7c
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <string.h>
+#include <stdlib.h>
+
+static void
+cc_parse_string(char *target, int len, char *source)
+{
+       strncpy(target, source, len-1);
+       target[len-1] = '\0';
+}
+
+static void
+cc_parse_int(int *target, char *source)
+{
+       *target = strtol(source, NULL, 0);
+}
+
+static void
+cc_parse_hex(int *target, char *source)
+{
+       *target = strtol(source, NULL, 16);
+}
+
+static void
+cc_parse_pos(double *target, char *source)
+{
+       int     deg;
+       double  min;
+       char    dir;
+       double  r;
+
+       if (sscanf(source, "%d°%lf'%c", &deg, &min, &dir) != 3) {
+               *target = 0;
+               return;
+       }
+       r = deg + min / 60.0;
+       if (dir == 'S' || dir == 'W')
+               r = -r;
+       *target = r;
+}
+
+#define PARSE_MAX_WORDS        512
+
+int
+cc_telem_parse(const char *input_line, struct cc_telem *telem)
+{
+       char *saveptr;
+       char *raw_words[PARSE_MAX_WORDS];
+       char **words;
+       int version = 0;
+       int nword;
+       char line_buf[8192], *line;
+       int     tracking_pos;
+
+       /* avoid smashing our input parameter */
+       strncpy (line_buf, input_line, sizeof (line_buf)-1);
+       line_buf[sizeof(line_buf) - 1] = '\0';
+       line = line_buf;
+       for (nword = 0; nword < PARSE_MAX_WORDS; nword++) {
+               raw_words[nword] = strtok_r(line, " \t\n", &saveptr);
+               line = NULL;
+               if (raw_words[nword] == NULL)
+                       break;
+       }
+       if (nword < 36)
+               return FALSE;
+       words = raw_words;
+       if (strcmp(words[0], "VERSION") == 0) {
+               cc_parse_int(&version, words[1]);
+               words += 2;
+               nword -= 2;
+       }
+
+       if (strcmp(words[0], "CALL") != 0)
+               return FALSE;
+       cc_parse_string(telem->callsign, sizeof (telem->callsign), words[1]);
+       cc_parse_int(&telem->serial, words[3]);
+
+       if (version >= 2) {
+               cc_parse_int(&telem->flight, words[5]);
+               words += 2;
+               nword -= 2;
+       } else
+               telem->flight = 0;
+
+       cc_parse_int(&telem->rssi, words[5]);
+       if (version <= 2) {
+               /* Older telemetry versions mis-computed the rssi value */
+               telem->rssi = (telem->rssi + 74) / 2 - 74;
+       }
+       cc_parse_string(telem->state, sizeof (telem->state), words[9]);
+       cc_parse_int(&telem->tick, words[10]);
+       cc_parse_int(&telem->accel, words[12]);
+       cc_parse_int(&telem->pres, words[14]);
+       cc_parse_int(&telem->temp, words[16]);
+       cc_parse_int(&telem->batt, words[18]);
+       cc_parse_int(&telem->drogue, words[20]);
+       cc_parse_int(&telem->main, words[22]);
+       cc_parse_int(&telem->flight_accel, words[24]);
+       cc_parse_int(&telem->ground_accel, words[26]);
+       cc_parse_int(&telem->flight_vel, words[28]);
+       cc_parse_int(&telem->flight_pres, words[30]);
+       cc_parse_int(&telem->ground_pres, words[32]);
+       if (version >= 1) {
+               cc_parse_int(&telem->accel_plus_g, words[34]);
+               cc_parse_int(&telem->accel_minus_g, words[36]);
+               words += 4;
+               nword -= 4;
+       } else {
+               telem->accel_plus_g = telem->ground_accel;
+               telem->accel_minus_g = telem->ground_accel + 530;
+       }
+       cc_parse_int(&telem->gps.nsat, words[34]);
+       if (strcmp (words[36], "unlocked") == 0) {
+               telem->gps.gps_connected = 1;
+               telem->gps.gps_locked = 0;
+               telem->gps.gps_time.year = telem->gps.gps_time.month = telem->gps.gps_time.day = 0;
+               telem->gps.gps_time.hour = telem->gps.gps_time.minute = telem->gps.gps_time.second = 0;
+               telem->gps.lat = telem->gps.lon = 0;
+               telem->gps.alt = 0;
+               tracking_pos = 37;
+       } else if (nword >= 40) {
+               telem->gps.gps_locked = 1;
+               telem->gps.gps_connected = 1;
+               if (version >= 2) {
+                       sscanf(words[36], "%d-%d-%d",
+                              &telem->gps.gps_time.year,
+                              &telem->gps.gps_time.month,
+                              &telem->gps.gps_time.day);
+                       words += 1;
+                       nword -= 1;
+               } else {
+                       telem->gps.gps_time.year = telem->gps.gps_time.month = telem->gps.gps_time.day = 0;
+               }
+               sscanf(words[36], "%d:%d:%d", &telem->gps.gps_time.hour, &telem->gps.gps_time.minute, &telem->gps.gps_time.second);
+               cc_parse_pos(&telem->gps.lat, words[37]);
+               cc_parse_pos(&telem->gps.lon, words[38]);
+               sscanf(words[39], "%dm", &telem->gps.alt);
+               tracking_pos = 46;
+       } else {
+               telem->gps.gps_connected = 0;
+               telem->gps.gps_locked = 0;
+               telem->gps.gps_time.year = telem->gps.gps_time.month = telem->gps.gps_time.day = 0;
+               telem->gps.gps_time.hour = telem->gps.gps_time.minute = telem->gps.gps_time.second = 0;
+               telem->gps.lat = telem->gps.lon = 0;
+               telem->gps.alt = 0;
+               tracking_pos = -1;
+       }
+       if (nword >= 46) {
+               telem->gps.gps_extended = 1;
+               sscanf(words[40], "%lfm/s", &telem->gps.ground_speed);
+               sscanf(words[41], "%d", &telem->gps.course);
+               sscanf(words[42], "%lfm/s", &telem->gps.climb_rate);
+               sscanf(words[43], "%lf", &telem->gps.hdop);
+               sscanf(words[44], "%d", &telem->gps.h_error);
+               sscanf(words[45], "%d", &telem->gps.v_error);
+       } else {
+               telem->gps.gps_extended = 0;
+               telem->gps.ground_speed = 0;
+               telem->gps.course = 0;
+               telem->gps.climb_rate = 0;
+               telem->gps.hdop = 0;
+               telem->gps.h_error = 0;
+               telem->gps.v_error = 0;
+       }
+       if (tracking_pos >= 0 && nword >= tracking_pos + 2 && strcmp(words[tracking_pos], "SAT") == 0) {
+               int     c, n, pos;
+               int     per_sat;
+               int     state;
+
+               if (version >= 2)
+                       per_sat = 2;
+               else
+                       per_sat = 3;
+               cc_parse_int(&n, words[tracking_pos + 1]);
+               pos = tracking_pos + 2;
+               if (nword >= pos + n * per_sat) {
+                       telem->gps_tracking.channels = n;
+                       for (c = 0; c < n; c++) {
+                               cc_parse_int(&telem->gps_tracking.sats[c].svid,
+                                                words[pos + 0]);
+                               if (version < 2)
+                                       cc_parse_hex(&state, words[pos + 1]);
+                               cc_parse_int(&telem->gps_tracking.sats[c].c_n0,
+                                                words[pos + per_sat - 1]);
+                               pos += per_sat;
+                       }
+               } else {
+                       telem->gps_tracking.channels = 0;
+               }
+       } else {
+               telem->gps_tracking.channels = 0;
+       }
+       return TRUE;
+}
diff --git a/ao-tools/lib/cc-telemetry.c b/ao-tools/lib/cc-telemetry.c
new file mode 100644 (file)
index 0000000..99da268
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "cc.h"
+#include <string.h>
+
+static int
+parse_byte(char *data, uint8_t *byte)
+{
+       char    d[3];
+       int x;
+       d[0] = data[0];
+       d[1] = data[1];
+       d[2] = '\0';
+
+       if (sscanf(d, "%x", &x) != 1)
+               return FALSE;
+       *byte = x;
+       return TRUE;
+}
+
+int
+cc_telemetry_parse(const char *input_line, union ao_telemetry_all *telemetry)
+{
+       uint8_t *byte;
+       char *data;
+       uint8_t hex[35];
+       int i;
+
+       if (strncmp(input_line, "TELEM", 5) != 0)
+               return FALSE;
+
+       data = strchr (input_line, ' ');
+       if (!data)
+               return FALSE;
+       data++;
+       byte = hex;
+       for (i = 0; i < 35; i++) {
+               if (!parse_byte(data, byte))
+                       return FALSE;
+               data += 2;
+               byte++;
+       }
+       if (hex[0] != 34)
+               return FALSE;
+       memcpy(telemetry, hex+1, 34);
+       return TRUE;
+}
diff --git a/ao-tools/lib/cc-telemetry.h b/ao-tools/lib/cc-telemetry.h
new file mode 100644 (file)
index 0000000..e849cd3
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _CC_TELEMETRY_H_
+#define _CC_TELEMETRY_H_
+/*
+ * ao_telemetry.c
+ */
+#define AO_MAX_CALLSIGN                        8
+#define AO_MAX_VERSION                 8
+#define AO_MAX_TELEMETRY               128
+
+struct ao_telemetry_generic {
+       uint16_t        serial;         /* 0 */
+       uint16_t        tick;           /* 2 */
+       uint8_t         type;           /* 4 */
+       uint8_t         payload[27];    /* 5 */
+       uint8_t         rssi;           /* 32 */
+       uint8_t         status;         /* 33 */
+       /* 34 */
+};
+
+#define AO_TELEMETRY_SENSOR_TELEMETRUM 0x01
+#define AO_TELEMETRY_SENSOR_TELEMINI   0x02
+#define AO_TELEMETRY_SENSOR_TELENANO   0x03
+
+struct ao_telemetry_sensor {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         state;          /*  5 flight state */
+       int16_t         accel;          /*  6 accelerometer (TM only) */
+       int16_t         pres;           /*  8 pressure sensor */
+       int16_t         temp;           /* 10 temperature sensor */
+       int16_t         v_batt;         /* 12 battery voltage */
+       int16_t         sense_d;        /* 14 drogue continuity sense (TM/Tm) */
+       int16_t         sense_m;        /* 16 main continuity sense (TM/Tm) */
+
+       int16_t         acceleration;   /* 18 m/s² * 16 */
+       int16_t         speed;          /* 20 m/s * 16 */
+       int16_t         height;         /* 22 m */
+
+       int16_t         ground_pres;    /* 24 average pres on pad */
+       int16_t         ground_accel;   /* 26 average accel on pad */
+       int16_t         accel_plus_g;   /* 28 accel calibration at +1g */
+       int16_t         accel_minus_g;  /* 30 accel calibration at -1g */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_CONFIGURATION     0x04
+
+struct ao_telemetry_configuration {
+       uint16_t        serial;                         /*  0 */
+       uint16_t        tick;                           /*  2 */
+       uint8_t         type;                           /*  4 */
+
+       uint8_t         device;                         /*  5 device type */
+       uint16_t        flight;                         /*  6 flight number */
+       uint8_t         config_major;                   /*  8 Config major version */
+       uint8_t         config_minor;                   /*  9 Config minor version */
+       uint16_t        apogee_delay;                   /* 10 Apogee deploy delay in seconds */
+       uint16_t        main_deploy;                    /* 12 Main deploy alt in meters */
+       uint16_t        flight_log_max;                 /* 14 Maximum flight log size in kB */
+       char            callsign[AO_MAX_CALLSIGN];      /* 16 Radio operator identity */
+       char            version[AO_MAX_VERSION];        /* 24 Software version */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_LOCATION          0x05
+
+#define AO_GPS_MODE_NOT_VALID          'N'
+#define AO_GPS_MODE_AUTONOMOUS         'A'
+#define AO_GPS_MODE_DIFFERENTIAL       'D'
+#define AO_GPS_MODE_ESTIMATED          'E'
+#define AO_GPS_MODE_MANUAL             'M'
+#define AO_GPS_MODE_SIMULATED          'S'
+
+struct ao_telemetry_location {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         flags;          /*  5 Number of sats and other flags */
+       int16_t         altitude;       /*  6 GPS reported altitude (m) */
+       int32_t         latitude;       /*  8 latitude (degrees * 10⁷) */
+       int32_t         longitude;      /* 12 longitude (degrees * 10⁷) */
+       uint8_t         year;           /* 16 (- 2000) */
+       uint8_t         month;          /* 17 (1-12) */
+       uint8_t         day;            /* 18 (1-31) */
+       uint8_t         hour;           /* 19 (0-23) */
+       uint8_t         minute;         /* 20 (0-59) */
+       uint8_t         second;         /* 21 (0-59) */
+       uint8_t         pdop;           /* 22 (m * 5) */
+       uint8_t         hdop;           /* 23 (m * 5) */
+       uint8_t         vdop;           /* 24 (m * 5) */
+       uint8_t         mode;           /* 25 */
+       uint16_t        ground_speed;   /* 26 cm/s */
+       int16_t         climb_rate;     /* 28 cm/s */
+       uint8_t         course;         /* 30 degrees / 2 */
+       uint8_t         unused[1];      /* 31 */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_SATELLITE         0x06
+
+struct ao_telemetry_satellite_info {
+       uint8_t         svid;
+       uint8_t         c_n_1;
+};
+
+struct ao_telemetry_satellite {
+       uint16_t                                serial;         /*  0 */
+       uint16_t                                tick;           /*  2 */
+       uint8_t                                 type;           /*  4 */
+       uint8_t                                 channels;       /*  5 number of reported sats */
+
+       struct ao_telemetry_satellite_info      sats[12];       /* 6 */
+       uint8_t                                 unused[2];      /* 30 */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_COMPANION         0x07
+
+#define AO_COMPANION_MAX_CHANNELS      12
+
+struct ao_telemetry_companion {
+       uint16_t                                serial;         /*  0 */
+       uint16_t                                tick;           /*  2 */
+       uint8_t                                 type;           /*  4 */
+       uint8_t                                 board_id;       /*  5 */
+
+       uint8_t                                 update_period;  /*  6 */
+       uint8_t                                 channels;       /*  7 */
+       uint16_t                                companion_data[AO_COMPANION_MAX_CHANNELS];      /*  8 */
+       /* 32 */
+};
+       
+#define AO_TELEMETRY_MEGA_SENSOR       0x08
+
+struct ao_telemetry_mega_sensor {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         pad5;           /*  5 */
+       int16_t         accel;          /*  6 Z axis */
+
+       int32_t         pres;           /*  8 Pa * 10 */
+       int16_t         temp;           /* 12 °C * 100 */
+
+       int16_t         accel_x;        /* 14 */
+       int16_t         accel_y;        /* 16 */
+       int16_t         accel_z;        /* 18 */
+
+       int16_t         gyro_x;         /* 20 */
+       int16_t         gyro_y;         /* 22 */
+       int16_t         gyro_z;         /* 24 */
+
+       int16_t         mag_x;          /* 26 */
+       int16_t         mag_y;          /* 28 */
+       int16_t         mag_z;          /* 30 */
+       /* 32 */
+};
+       
+#define AO_TELEMETRY_MEGA_DATA         0x09
+
+struct ao_telemetry_mega_data {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         state;          /*  5 flight state */
+
+       int16_t         v_batt;         /*  6 battery voltage */
+       int16_t         v_pyro;         /*  8 pyro battery voltage */
+       int8_t          sense[6];       /* 10 continuity sense */
+
+       int32_t         ground_pres;    /* 16 average pres on pad */
+       int16_t         ground_accel;   /* 20 average accel on pad */
+       int16_t         accel_plus_g;   /* 22 accel calibration at +1g */
+       int16_t         accel_minus_g;  /* 24 accel calibration at -1g */
+
+       int16_t         acceleration;   /* 26 m/s² * 16 */
+       int16_t         speed;          /* 28 m/s * 16 */
+       int16_t         height;         /* 30 m */
+       /* 32 */
+};
+
+/* #define AO_SEND_ALL_BARO */
+
+#define AO_TELEMETRY_BARO              0x80
+
+/*
+ * This packet allows the full sampling rate baro
+ * data to be captured over the RF link so that the
+ * flight software can be tested using 'real' data.
+ *
+ * Along with this telemetry packet, the flight
+ * code is modified to send full-rate telemetry all the time
+ * and never send an RDF tone; this ensure that the full radio
+ * link is available.
+ */
+struct ao_telemetry_baro {
+       uint16_t                                serial;         /*  0 */
+       uint16_t                                tick;           /*  2 */
+       uint8_t                                 type;           /*  4 */
+       uint8_t                                 samples;        /*  5 number samples */
+
+       int16_t                                 baro[12];       /* 6 samples */
+       /* 32 */
+};
+
+union ao_telemetry_all {
+       struct ao_telemetry_generic             generic;
+       struct ao_telemetry_sensor              sensor;
+       struct ao_telemetry_configuration       configuration;
+       struct ao_telemetry_location            location;
+       struct ao_telemetry_satellite           satellite;
+       struct ao_telemetry_companion           companion;
+       struct ao_telemetry_mega_sensor         mega_sensor;
+       struct ao_telemetry_mega_data           mega_data;
+       struct ao_telemetry_baro                baro;
+};
+
+int
+cc_telemetry_parse(const char *input_line, union ao_telemetry_all *telemetry);
+
+#endif
index 81309983fd46b7487aaba7cabc005051373322be..1580c6d9ffee488f84e1910e3e1cc9ecec943938 100644 (file)
 #include "cc-usb.h"
 
 
-#define CC_NUM_READ            16
+#define CC_NUM_HEX_READ                64
 /*
  * AltOS has different buffer sizes for in/out packets
  */
-#define CC_IN_BUF              256
+#define CC_IN_BUF              65536
 #define CC_OUT_BUF             64
 #define DEFAULT_TTY            "/dev/ttyACM0"
 
-struct cc_read {
+struct cc_hex_read {
        uint8_t *buf;
        int     len;
 };
 
 struct cc_usb {
-       int             fd;
-       uint8_t         in_buf[CC_IN_BUF];
-       int             in_count;
-       uint8_t         out_buf[CC_OUT_BUF];
-       int             out_count;
-       struct cc_read  read_buf[CC_NUM_READ];
-       int             read_count;
+       int                     fd;
+       uint8_t                 in_buf[CC_IN_BUF];
+       int                     in_pos;
+       int                     in_count;
+       uint8_t                 out_buf[CC_OUT_BUF];
+       int                     out_count;
+
+       struct cc_hex_read      hex_buf[CC_NUM_HEX_READ];
+       int                     hex_count;
+
+       int                     remote;
 };
 
 #define NOT_HEX        0xff
@@ -72,61 +76,48 @@ cc_hex_nibble(uint8_t c)
  * and write them to the waiting buffer
  */
 static void
-cc_handle_in(struct cc_usb *cc)
+cc_handle_hex_read(struct cc_usb *cc)
 {
        uint8_t h, l;
-       int     in_pos;
-       int     read_pos;
+       int     hex_pos;
 
-       in_pos = 0;
-       read_pos = 0;
-       while (read_pos < cc->read_count && in_pos < cc->in_count) {
+       hex_pos = 0;
+       while (hex_pos < cc->hex_count && cc->in_pos < cc->in_count) {
                /*
                 * Skip to next hex character
                 */
-               while (in_pos < cc->in_count &&
-                      cc_hex_nibble(cc->in_buf[in_pos]) == NOT_HEX)
-                       in_pos++;
+               while (cc->in_pos < cc->in_count &&
+                      cc_hex_nibble(cc->in_buf[cc->in_pos]) == NOT_HEX)
+                       cc->in_pos++;
                /*
                 * Make sure we have two characters left
                 */
-               if (cc->in_count - in_pos < 2)
+               if (cc->in_count - cc->in_pos < 2)
                        break;
                /*
                 * Parse hex number
                 */
-               h = cc_hex_nibble(cc->in_buf[in_pos]);
-               l = cc_hex_nibble(cc->in_buf[in_pos+1]);
+               h = cc_hex_nibble(cc->in_buf[cc->in_pos]);
+               l = cc_hex_nibble(cc->in_buf[cc->in_pos+1]);
                if (h == NOT_HEX || l == NOT_HEX) {
                        fprintf(stderr, "hex read error\n");
                        break;
                }
-               in_pos += 2;
+               cc->in_pos += 2;
                /*
                 * Store hex number
                 */
-               *cc->read_buf[read_pos].buf++ = (h << 4) | l;
-               if (--cc->read_buf[read_pos].len <= 0)
-                       read_pos++;
-       }
-
-       /* Move remaining bytes to the start of the input buffer */
-       if (in_pos) {
-               memmove(cc->in_buf, cc->in_buf + in_pos,
-                       cc->in_count - in_pos);
-               cc->in_count -= in_pos;
+               *cc->hex_buf[hex_pos].buf++ = (h << 4) | l;
+               if (--cc->hex_buf[hex_pos].len <= 0)
+                       hex_pos++;
        }
 
-       /* Move pending reads to the start of the array */
-       if (read_pos) {
-               memmove(cc->read_buf, cc->read_buf + read_pos,
-                       (cc->read_count - read_pos) * sizeof (cc->read_buf[0]));
-               cc->read_count -= read_pos;
+       /* Move pending hex reads to the start of the array */
+       if (hex_pos) {
+               memmove(cc->hex_buf, cc->hex_buf + hex_pos,
+                       (cc->hex_count - hex_pos) * sizeof (cc->hex_buf[0]));
+               cc->hex_count -= hex_pos;
        }
-
-       /* Once we're done reading, flush any pending input */
-       if (cc->read_count == 0)
-               cc->in_count = 0;
 }
 
 static void
@@ -157,8 +148,9 @@ cc_usb_dbg(int indent, uint8_t *bytes, int len)
 /*
  * Flush pending writes, fill pending reads
  */
-void
-cc_usb_sync(struct cc_usb *cc)
+
+static int
+_cc_usb_sync(struct cc_usb *cc, int wait_for_input)
 {
        int             ret;
        struct pollfd   fds;
@@ -166,21 +158,33 @@ cc_usb_sync(struct cc_usb *cc)
 
        fds.fd = cc->fd;
        for (;;) {
-               if (cc->read_count || cc->out_count)
-                       timeout = -1;
+               if (cc->hex_count || cc->out_count)
+                       timeout = 5000;
+               else if (wait_for_input && cc->in_pos == cc->in_count)
+                       timeout = wait_for_input;
                else
                        timeout = 0;
                fds.events = 0;
+               /* Move remaining bytes to the start of the input buffer */
+               if (cc->in_pos) {
+                       memmove(cc->in_buf, cc->in_buf + cc->in_pos,
+                               cc->in_count - cc->in_pos);
+                       cc->in_count -= cc->in_pos;
+                       cc->in_pos = 0;
+               }
                if (cc->in_count < CC_IN_BUF)
                        fds.events |= POLLIN;
                if (cc->out_count)
                        fds.events |= POLLOUT;
                ret = poll(&fds, 1, timeout);
-               if (ret == 0)
+               if (ret == 0) {
+                       if (timeout)
+                               return -1;
                        break;
+               }
                if (ret < 0) {
                        perror("poll");
-                       break;
+                       return -1;
                }
                if (fds.revents & POLLIN) {
                        ret = read(cc->fd, cc->in_buf + cc->in_count,
@@ -188,7 +192,8 @@ cc_usb_sync(struct cc_usb *cc)
                        if (ret > 0) {
                                cc_usb_dbg(24, cc->in_buf + cc->in_count, ret);
                                cc->in_count += ret;
-                               cc_handle_in(cc);
+                               if (cc->hex_count)
+                                       cc_handle_hex_read(cc);
                        } else if (ret < 0)
                                perror("read");
                }
@@ -205,6 +210,16 @@ cc_usb_sync(struct cc_usb *cc)
                                perror("write");
                }
        }
+       return 0;
+}
+
+void
+cc_usb_sync(struct cc_usb *cc)
+{
+       if (_cc_usb_sync(cc, 0) < 0) {
+               fprintf(stderr, "USB link timeout\n");
+               exit(1);
+       }
 }
 
 void
@@ -238,6 +253,38 @@ cc_usb_printf(struct cc_usb *cc, char *format, ...)
        }
 }
 
+int
+cc_usb_getchar(struct cc_usb *cc)
+{
+       while (cc->in_pos == cc->in_count) {
+               if (_cc_usb_sync(cc, 5000) < 0) {
+                       fprintf(stderr, "USB link timeout\n");
+                       exit(1);
+               }
+       }
+       return cc->in_buf[cc->in_pos++];
+}
+
+void
+cc_usb_getline(struct cc_usb *cc, char *line, int max)
+{
+       int     c;
+
+       while ((c = cc_usb_getchar(cc)) != '\n') {
+               switch (c) {
+               case '\r':
+                       break;
+               default:
+                       if (max > 1) {
+                               *line++ = c;
+                               max--;
+                       }
+                       break;
+               }
+       }
+       *line++ = '\0';
+}
+
 int
 cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len)
 {
@@ -260,12 +307,18 @@ cc_usb_send_bytes(struct cc_usb *cc, uint8_t *bytes, int len)
 void
 cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len)
 {
-       struct cc_read  *read_buf;
-       while (cc->read_count >= CC_NUM_READ)
+       struct cc_hex_read      *hex_buf;
+
+       /* At the start of a command sequence, flush any pending input */
+       if (cc->hex_count == 0) {
+               cc_usb_sync(cc);
+               cc->in_count = 0;
+       }
+       while (cc->hex_count >= CC_NUM_HEX_READ)
                cc_usb_sync(cc);
-       read_buf = &cc->read_buf[cc->read_count++];
-       read_buf->buf = buf;
-       read_buf->len = len;
+       hex_buf = &cc->hex_buf[cc->hex_count++];
+       hex_buf->buf = buf;
+       hex_buf->len = len;
 }
 
 int
@@ -321,6 +374,29 @@ cc_usb_reset(struct cc_usb *cc)
        return 1;
 }
 
+void
+cc_usb_open_remote(struct cc_usb *cc, int channel)
+{
+       if (!cc->remote) {
+               printf ("channel %d\n", channel);
+               cc_usb_printf(cc, "\nc r %d\np\nE 0\n", channel);
+               do {
+                       cc->in_count = cc->in_pos = 0;
+                       _cc_usb_sync(cc, 100);
+               } while (cc->in_count > 0);
+               cc->remote = 1;
+       }
+}
+
+void
+cc_usb_close_remote(struct cc_usb *cc)
+{
+       if (cc->remote) {
+               cc_usb_printf(cc, "~");
+               cc->remote = 0;
+       }
+}
+
 static struct termios  save_termios;
 
 struct cc_usb *
@@ -344,16 +420,19 @@ cc_usb_open(char *tty)
        save_termios = termios;
        cfmakeraw(&termios);
        tcsetattr(cc->fd, TCSAFLUSH, &termios);
-       cc_usb_printf(cc, "E 0\nm 0\n");
-       cc_usb_sync(cc);
-       sleep(1);
-       cc_usb_sync(cc);
+       cc_usb_printf(cc, "\nE 0\nm 0\n");
+       do {
+               cc->in_count = cc->in_pos = 0;
+               _cc_usb_sync(cc, 100);
+       } while (cc->in_count > 0);
        return cc;
 }
 
 void
 cc_usb_close(struct cc_usb *cc)
 {
+       cc_usb_close_remote(cc);
+       cc_usb_sync(cc);
        tcsetattr(cc->fd, TCSAFLUSH, &save_termios);
        close (cc->fd);
        free (cc);
index d7acfbd2be1b70a1e59283b2af582e868c0db701..d3539281f83aa4001c22a696a8eb15c90e69160f 100644 (file)
@@ -53,7 +53,19 @@ cc_usb_sync(struct cc_usb *cc);
 void
 cc_queue_read(struct cc_usb *cc, uint8_t *buf, int len);
 
+int
+cc_usb_getchar(struct cc_usb *cc);
+
+void
+cc_usb_getline(struct cc_usb *cc, char *line, int max);
+
 void
 cc_usb_printf(struct cc_usb *cc, char *format, ...);
 
+void
+cc_usb_open_remote(struct cc_usb *cc, int channel);
+
+void
+cc_usb_close_remote(struct cc_usb *cc);
+
 #endif /* _CC_USB_H_ */
diff --git a/ao-tools/lib/cc-usbdev.c b/ao-tools/lib/cc-usbdev.c
new file mode 100644 (file)
index 0000000..a19e231
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define _GNU_SOURCE
+#include "cc.h"
+#include <ctype.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static char *
+load_string(char *dir, char *file)
+{
+       char    *full = cc_fullname(dir, file);
+       char    line[4096];
+       char    *r;
+       FILE    *f;
+       int     rlen;
+
+       f = fopen(full, "r");
+       free(full);
+       if (!f)
+               return NULL;
+       r = fgets(line, sizeof (line), f);
+       fclose(f);
+       if (!r)
+               return NULL;
+       rlen = strlen(r);
+       if (r[rlen-1] == '\n')
+               r[rlen-1] = '\0';
+       return strdup(r);
+}
+
+static int
+load_hex(char *dir, char *file)
+{
+       char    *line;
+       char    *end;
+       long    i;
+
+       line = load_string(dir, file);
+       if (!line)
+               return -1;
+       i = strtol(line, &end, 16);
+       free(line);
+       if (end == line)
+               return -1;
+       return i;
+}
+
+static int
+load_dec(char *dir, char *file)
+{
+       char    *line;
+       char    *end;
+       long    i;
+
+       line = load_string(dir, file);
+       if (!line)
+               return -1;
+       i = strtol(line, &end, 10);
+       free(line);
+       if (end == line)
+               return -1;
+       return i;
+}
+
+static int
+dir_filter_tty_colon(const struct dirent *d)
+{
+       return strncmp(d->d_name, "tty:", 4) == 0;
+}
+
+static int
+dir_filter_tty(const struct dirent *d)
+{
+       return strncmp(d->d_name, "tty", 3) == 0;
+}
+
+static char *
+usb_tty(char *sys)
+{
+       char *base;
+       int num_configs;
+       int config;
+       struct dirent **namelist;
+       int interface;
+       int num_interfaces;
+       char endpoint_base[20];
+       char *endpoint_full;
+       char *tty_dir;
+       int ntty;
+       char *tty;
+
+       base = cc_basename(sys);
+       num_configs = load_hex(sys, "bNumConfigurations");
+       num_interfaces = load_hex(sys, "bNumInterfaces");
+       for (config = 1; config <= num_configs; config++) {
+               for (interface = 0; interface < num_interfaces; interface++) {
+                       sprintf(endpoint_base, "%s:%d.%d",
+                               base, config, interface);
+                       endpoint_full = cc_fullname(sys, endpoint_base);
+
+                       /* Check for tty:ttyACMx style names
+                        */
+                       ntty = scandir(endpoint_full, &namelist,
+                                      dir_filter_tty_colon,
+                                      alphasort);
+                       if (ntty > 0) {
+                               free(endpoint_full);
+                               tty = cc_fullname("/dev", namelist[0]->d_name + 4);
+                               free(namelist);
+                               return tty;
+                       }
+
+                       /* Check for tty/ttyACMx style names
+                        */
+                       tty_dir = cc_fullname(endpoint_full, "tty");
+                       free(endpoint_full);
+                       ntty = scandir(tty_dir, &namelist,
+                                      dir_filter_tty,
+                                      alphasort);
+                       free (tty_dir);
+                       if (ntty > 0) {
+                               tty = cc_fullname("/dev", namelist[0]->d_name);
+                               free(namelist);
+                               return tty;
+                       }
+               }
+       }
+       return NULL;
+}
+
+static struct cc_usbdev *
+usb_scan_device(char *sys)
+{
+       struct cc_usbdev *usbdev;
+
+       usbdev = calloc(1, sizeof (struct cc_usbdev));
+       if (!usbdev)
+               return NULL;
+       usbdev->sys = strdup(sys);
+       usbdev->manufacturer = load_string(sys, "manufacturer");
+       usbdev->product = load_string(sys, "product");
+       usbdev->serial = load_dec(sys, "serial");
+       usbdev->idProduct = load_hex(sys, "idProduct");
+       usbdev->idVendor = load_hex(sys, "idVendor");
+       usbdev->tty = usb_tty(sys);
+       return usbdev;
+}
+
+static void
+usbdev_free(struct cc_usbdev *usbdev)
+{
+       free(usbdev->sys);
+       free(usbdev->manufacturer);
+       free(usbdev->product);
+       /* this can get used as a return value */
+       if (usbdev->tty)
+               free(usbdev->tty);
+       free(usbdev);
+}
+
+#define USB_DEVICES    "/sys/bus/usb/devices"
+
+static int
+dir_filter_dev(const struct dirent *d)
+{
+       const char      *n = d->d_name;
+       char    c;
+
+       while ((c = *n++)) {
+               if (isdigit(c))
+                       continue;
+               if (c == '-')
+                       continue;
+               if (c == '.' && n != d->d_name + 1)
+                       continue;
+               return 0;
+       }
+       return 1;
+}
+
+struct cc_usbdevs *
+cc_usbdevs_scan(void)
+{
+       int                     e;
+       struct dirent           **ents;
+       char                    *dir;
+       struct cc_usbdev        *dev;
+       struct cc_usbdevs       *devs;
+       int                     n;
+
+       devs = calloc(1, sizeof (struct cc_usbdevs));
+       if (!devs)
+               return NULL;
+
+       n = scandir (USB_DEVICES, &ents,
+                    dir_filter_dev,
+                    alphasort);
+       if (!n)
+               return 0;
+       for (e = 0; e < n; e++) {
+               dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
+               dev = usb_scan_device(dir);
+               free(dir);
+               if (dev->idVendor == 0xfffe && dev->tty) {
+                       if (devs->dev)
+                               devs->dev = realloc(devs->dev,
+                                                   (devs->ndev + 1) * sizeof (struct usbdev *));
+                       else
+                               devs->dev = malloc (sizeof (struct usbdev *));
+                       devs->dev[devs->ndev++] = dev;
+               }
+       }
+       free(ents);
+       return devs;
+}
+
+void
+cc_usbdevs_free(struct cc_usbdevs *usbdevs)
+{
+       int     i;
+
+       if (!usbdevs)
+               return;
+       for (i = 0; i < usbdevs->ndev; i++)
+               usbdev_free(usbdevs->dev[i]);
+       free(usbdevs);
+}
+
+static char *
+match_dev(char *product, int serial)
+{
+       struct cc_usbdevs       *devs;
+       struct cc_usbdev        *dev;
+       int                     i;
+       char                    *tty = NULL;
+
+       devs = cc_usbdevs_scan();
+       if (!devs)
+               return NULL;
+       for (i = 0; i < devs->ndev; i++) {
+               dev = devs->dev[i];
+               if (product && strncmp (product, dev->product, strlen(product)) != 0)
+                       continue;
+               if (serial && serial != dev->serial)
+                       continue;
+               break;
+       }
+       if (i < devs->ndev) {
+               tty = devs->dev[i]->tty;
+               devs->dev[i]->tty = NULL;
+       }
+       cc_usbdevs_free(devs);
+       return tty;
+}
+
+char *
+cc_usbdevs_find_by_arg(char *arg, char *default_product)
+{
+       char    *product;
+       int     serial;
+       char    *end;
+       char    *colon;
+       char    *tty;
+
+       if (arg)
+       {
+               /* check for <serial> */
+               serial = strtol(arg, &end, 0);
+               if (end != arg) {
+                       if (*end != '\0')
+                               return NULL;
+                       product = NULL;
+               } else {
+                       /* check for <product>:<serial> */
+                       colon = strchr(arg, ':');
+                       if (colon) {
+                               product = strndup(arg, colon - arg);
+                               serial = strtol(colon + 1, &end, 0);
+                               if (*end != '\0')
+                                       return NULL;
+                       } else {
+                               product = arg;
+                               serial = 0;
+                       }
+               }
+       } else {
+               product = NULL;
+               serial = 0;
+       }
+       tty = NULL;
+       if (!product && default_product)
+               tty = match_dev(default_product, serial);
+       if (!tty)
+               tty = match_dev(product, serial);
+       if (product && product != arg)
+               free(product);
+       return tty;
+}
diff --git a/ao-tools/lib/cc-util.c b/ao-tools/lib/cc-util.c
new file mode 100644 (file)
index 0000000..65488ee
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define _GNU_SOURCE
+#include "cc.h"
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+char *
+cc_fullname (char *dir, char *file)
+{
+       char    *new;
+       int     dlen = strlen (dir);
+       int     flen = strlen (file);
+       int     slen = 0;
+
+       if (dir[dlen-1] != '/')
+               slen = 1;
+       new = malloc (dlen + slen + flen + 1);
+       if (!new)
+               return 0;
+       strcpy(new, dir);
+       if (slen)
+               strcat (new, "/");
+       strcat(new, file);
+       return new;
+}
+
+char *
+cc_basename(char *file)
+{
+       char *b;
+
+       b = strrchr(file, '/');
+       if (!b)
+               return file;
+       return b + 1;
+}
+
+int
+cc_mkdir(char *dir)
+{
+       char    *slash;
+       char    *d;
+       char    *part;
+
+       d = dir;
+       for (;;) {
+               slash = strchr (d, '/');
+               if (!slash)
+                       slash = d + strlen(d);
+               if (!*slash)
+                       break;
+               part = strndup(dir, slash - dir);
+               if (!access(part, F_OK))
+                       if (mkdir(part, 0777) < 0)
+                               return -errno;
+               free(part);
+               d = slash + 1;
+       }
+       return 0;
+}
diff --git a/ao-tools/lib/cc.h b/ao-tools/lib/cc.h
new file mode 100644 (file)
index 0000000..6257ee4
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _CC_H_
+#define _CC_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include "cc-telemetry.h"
+
+char *
+cc_fullname (char *dir, char *file);
+
+char *
+cc_basename(char *file);
+
+int
+cc_mkdir(char *dir);
+
+struct cc_usbdev {
+       char    *sys;
+       char    *tty;
+       char    *manufacturer;
+       char    *product;
+       int     serial; /* AltOS always uses simple integer serial numbers */
+       int     idProduct;
+       int     idVendor;
+};
+
+struct cc_usbdevs {
+       struct cc_usbdev        **dev;
+       int                     ndev;
+};
+
+void
+cc_usbdevs_free(struct cc_usbdevs *usbdevs);
+
+struct cc_usbdevs *
+cc_usbdevs_scan(void);
+
+char *
+cc_usbdevs_find_by_arg(char *arg, char *default_product);
+
+void
+cc_set_log_dir(char *dir);
+
+char *
+cc_get_log_dir(void);
+
+char *
+cc_make_filename(int serial, int flight, char *ext);
+
+/*
+ * For sequential data which are not evenly spaced
+ */
+
+struct cc_timedataelt {
+       double  time;
+       double  value;
+};
+
+struct cc_timedata {
+       int                     num;
+       int                     size;
+       struct cc_timedataelt   *data;
+       double                  time_offset;
+};
+
+
+/*
+ * For GPS data
+ */
+
+struct cc_gpselt {
+       double          time;
+       int             hour;
+       int             minute;
+       int             second;
+       int             flags;
+       double          lat;
+       double          lon;
+       double          alt;
+};
+
+#define SIRF_SAT_STATE_ACQUIRED                        (1 << 0)
+#define SIRF_SAT_STATE_CARRIER_PHASE_VALID     (1 << 1)
+#define SIRF_SAT_BIT_SYNC_COMPLETE             (1 << 2)
+#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE                (1 << 3)
+#define SIRF_SAT_CARRIER_PULLIN_COMPLETE       (1 << 4)
+#define SIRF_SAT_CODE_LOCKED                   (1 << 5)
+#define SIRF_SAT_ACQUISITION_FAILED            (1 << 6)
+#define SIRF_SAT_EPHEMERIS_AVAILABLE           (1 << 7)
+
+struct cc_gpssat {
+       double          time;
+       uint16_t        svid;
+       uint8_t         c_n;
+};
+
+struct cc_gpssats {
+       int                     nsat;
+       struct cc_gpssat        sat[12];
+};
+
+struct cc_gpsdata {
+       int                     num;
+       int                     size;
+       struct cc_gpselt        *data;
+       double                  time_offset;
+       int                     numsats;
+       int                     sizesats;
+       struct cc_gpssats       *sats;
+};
+
+/*
+ * For sequential data which are evenly spaced
+ */
+struct cc_perioddata {
+       int             num;
+       double          start;
+       double          step;
+       double          *data;
+};
+
+enum ao_flight_state {
+       ao_flight_startup = 0,
+       ao_flight_idle = 1,
+       ao_flight_pad = 2,
+       ao_flight_boost = 3,
+       ao_flight_fast = 4,
+       ao_flight_coast = 5,
+       ao_flight_drogue = 6,
+       ao_flight_main = 7,
+       ao_flight_landed = 8,
+       ao_flight_invalid = 9
+};
+
+struct cc_flightraw {
+       int                     flight;
+       int                     serial;
+       double                  ground_accel;
+       double                  ground_pres;
+       int                     year, month, day;
+       struct cc_timedata      accel;
+       struct cc_timedata      pres;
+       struct cc_timedata      temp;
+       struct cc_timedata      volt;
+       struct cc_timedata      main;
+       struct cc_timedata      drogue;
+       struct cc_timedata      state;
+       struct cc_gpsdata       gps;
+};
+
+struct cc_flightraw *
+cc_log_read(FILE *file);
+
+void
+cc_flightraw_free(struct cc_flightraw *raw);
+
+struct cc_flightcooked {
+       double                  flight_start;
+       double                  flight_stop;
+
+       struct cc_perioddata    accel_accel;
+       struct cc_perioddata    accel_speed;
+       struct cc_perioddata    accel_pos;
+       struct cc_perioddata    pres_pos;
+       struct cc_perioddata    pres_speed;
+       struct cc_perioddata    pres_accel;
+       struct cc_perioddata    gps_lat;
+       struct cc_perioddata    gps_lon;
+       struct cc_perioddata    gps_alt;
+
+       /* unfiltered, but converted */
+       struct cc_timedata      pres;
+       struct cc_timedata      accel;
+       struct cc_timedata      state;
+};
+
+/*
+ * Telemetry data contents
+ */
+
+
+struct cc_gps_time {
+       int year;
+       int month;
+       int day;
+       int hour;
+       int minute;
+       int second;
+};
+
+struct cc_gps {
+       int     nsat;
+       int     gps_locked;
+       int     gps_connected;
+       struct cc_gps_time gps_time;
+       double  lat;            /* degrees (+N -S) */
+       double  lon;            /* degrees (+E -W) */
+       int     alt;            /* m */
+
+       int     gps_extended;   /* has extra data */
+       double  ground_speed;   /* m/s */
+       int     course;         /* degrees */
+       double  climb_rate;     /* m/s */
+       double  hdop;           /* unitless? */
+       int     h_error;        /* m */
+       int     v_error;        /* m */
+};
+
+#define SIRF_SAT_STATE_ACQUIRED                        (1 << 0)
+#define SIRF_SAT_STATE_CARRIER_PHASE_VALID     (1 << 1)
+#define SIRF_SAT_BIT_SYNC_COMPLETE             (1 << 2)
+#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE                (1 << 3)
+#define SIRF_SAT_CARRIER_PULLIN_COMPLETE       (1 << 4)
+#define SIRF_SAT_CODE_LOCKED                   (1 << 5)
+#define SIRF_SAT_ACQUISITION_FAILED            (1 << 6)
+#define SIRF_SAT_EPHEMERIS_AVAILABLE           (1 << 7)
+
+struct cc_gps_sat {
+       int     svid;
+       int     c_n0;
+};
+
+struct cc_gps_tracking {
+       int                     channels;
+       struct cc_gps_sat       sats[12];
+};
+
+struct cc_telem {
+       char    callsign[16];
+       int     serial;
+       int     flight;
+       int     rssi;
+       char    state[16];
+       int     tick;
+       int     accel;
+       int     pres;
+       int     temp;
+       int     batt;
+       int     drogue;
+       int     main;
+       int     flight_accel;
+       int     ground_accel;
+       int     flight_vel;
+       int     flight_pres;
+       int     ground_pres;
+       int     accel_plus_g;
+       int     accel_minus_g;
+       struct cc_gps   gps;
+       struct cc_gps_tracking  gps_tracking;
+};
+
+int
+cc_telem_parse(const char *input_line, struct cc_telem *telem);
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* Conversion functions */
+double
+cc_pressure_to_altitude(double pressure);
+
+double
+cc_altitude_to_pressure(double altitude);
+
+double
+cc_barometer_to_pressure(double baro);
+
+double
+cc_barometer_to_altitude(double baro);
+
+double
+cc_accelerometer_to_acceleration(double accel, double ground_accel);
+
+double
+cc_thermometer_to_temperature(double thermo);
+
+double
+cc_battery_to_voltage(double battery);
+
+double
+cc_ignitor_to_voltage(double ignite);
+
+void
+cc_great_circle (double start_lat, double start_lon,
+                double end_lat, double end_lon,
+                double *dist, double *bearing);
+
+void
+cc_timedata_limits(struct cc_timedata *d, double min_time, double max_time, int *start, int *stop);
+
+int
+cc_timedata_min(struct cc_timedata *d, double min_time, double max_time);
+
+int
+cc_timedata_min_mag(struct cc_timedata *d, double min_time, double max_time);
+
+int
+cc_timedata_max(struct cc_timedata *d, double min_time, double max_time);
+
+int
+cc_timedata_max_mag(struct cc_timedata *d, double min_time, double max_time);
+
+double
+cc_timedata_average(struct cc_timedata *d, double min_time, double max_time);
+
+double
+cc_timedata_average_mag(struct cc_timedata *d, double min_time, double max_time);
+
+int
+cc_perioddata_limits(struct cc_perioddata *d, double min_time, double max_time, int *start, int *stop);
+
+int
+cc_perioddata_min(struct cc_perioddata *d, double min_time, double max_time);
+
+int
+cc_perioddata_min_mag(struct cc_perioddata *d, double min_time, double max_time);
+
+int
+cc_perioddata_max(struct cc_perioddata *d, double min_time, double max_time);
+
+int
+cc_perioddata_max_mag(struct cc_perioddata *d, double min_time, double max_time);
+
+double
+cc_perioddata_average(struct cc_perioddata *d, double min_time, double max_time);
+
+double
+cc_perioddata_average_mag(struct cc_perioddata *d, double min_time, double max_time);
+
+double *
+cc_low_pass(double *data, int data_len, double omega_pass, double omega_stop, double error);
+
+struct cc_perioddata *
+cc_period_make(struct cc_timedata *td, double start_time, double stop_time);
+
+struct cc_perioddata *
+cc_period_low_pass(struct cc_perioddata *raw, double omega_pass, double omega_stop, double error);
+
+struct cc_timedata *
+cc_timedata_convert(struct cc_timedata *d, double (*f)(double v, double a), double a);
+
+struct cc_timedata *
+cc_timedata_integrate(struct cc_timedata *d, double min_time, double max_time);
+
+struct cc_perioddata *
+cc_perioddata_differentiate(struct cc_perioddata *i);
+
+struct cc_flightcooked *
+cc_flight_cook(struct cc_flightraw *raw);
+
+void
+cc_flightcooked_free(struct cc_flightcooked *cooked);
+
+#endif /* _CC_H_ */
diff --git a/ao-tools/lib/cephes.h b/ao-tools/lib/cephes.h
new file mode 100644 (file)
index 0000000..f8ec264
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * This file comes from the cephes math library, which was
+ * released under the GPLV2+ license as a part of the Debian labplot
+ * package (I've included the GPLV2 license reference here to make
+ * this clear) - Keith Packard <keithp@keithp.com>
+ *
+ * Cephes Math Library Release 2.0:  April, 1987
+ * Copyright 1984, 1987 by Stephen L. Moshier
+ * Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+ *
+ * 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.
+ */
+/*
+ * Prototypes of Cephes functions
+ */
+
+#ifndef _CEPHES_H_
+#define _CEPHES_H_
+
+/* Variable for error reporting.  See mtherr.c.  */
+extern int merror;
+
+#if 0
+extern int airy ( double x, double *ai, double *aip, double *bi, double *bip );
+extern double beta ( double a, double b );
+extern double lbeta ( double a, double b );
+extern double chdtrc ( double df, double x );
+extern double chdtr ( double df, double x );
+extern double chdtri ( double df, double y );
+extern double dawsn ( double xx );
+extern double ellie ( double phi, double m );
+extern double ellik ( double phi, double m );
+extern double ellpe ( double x );
+extern double ellpk ( double x );
+extern double expn ( int n, double x );
+extern double fac ( int i );
+extern double fdtrc ( int ia, int ib, double x );
+extern double fdtr ( int ia, int ib, double x );
+extern double fdtri ( int ia, int ib, double y );
+extern double frexp ( double x, int *pw2 );
+extern double ldexp ( double x, int pw2 );
+extern int fresnl ( double xxa, double *ssa, double *cca );
+extern double gdtr ( double a, double b, double x );
+extern double gdtrc ( double a, double b, double x );
+extern double hyp2f0 ( double a, double b, double x, int type, double *err );
+extern double hyp2f1 ( double a, double b, double c, double x );
+extern double hyperg ( double a, double b, double x );
+#endif
+extern double i0 ( double x );
+extern double i0e ( double x );
+#if 0
+extern double i1 ( double x );
+extern double i1e ( double x );
+extern double iv ( double v, double x );
+extern double igamc ( double a, double x );
+extern double igam ( double a, double x );
+extern double igami ( double a, double y0_ );
+extern double incbet ( double aa, double bb, double xx );
+extern double incbi ( double aa, double bb, double yy0 );
+extern double jv ( double n, double x );
+extern double k0 ( double x );
+extern double k0e ( double x );
+extern double k1 ( double x );
+extern double k1e ( double x );
+extern double kn ( int nn, double x );
+extern int mtherr ( char *name, int code );
+extern double ndtr ( double a );
+extern double ndtri ( double y0_ );
+extern double pdtrc ( int k, double m );
+extern double pdtr ( int k, double m );
+extern double pdtri ( int k, double y );
+extern double psi ( double x );
+extern void revers ( double y[], double x[], int n );
+extern double true_gamma ( double x );
+extern double rgamma ( double x );
+extern int shichi ( double x, double *si, double *ci );
+extern int sici ( double x, double *si, double *ci );
+extern double spence ( double x );
+extern double stdtr ( int k, double t );
+extern double stdtri ( int k, double p );
+extern double onef2 ( double a, double b, double c, double x, double *err );
+extern double threef0 ( double a, double b, double c, double x, double *err );
+extern double struve ( double v, double x );
+extern double log1p ( double x );
+extern double expm1 ( double x );
+extern double cosm1 ( double x );
+extern double yv ( double v, double x );
+extern double zeta ( double x, double q );
+extern double zetac ( double x );
+
+#endif
+extern double chbevl ( double x, void *P, int n );
+#if 0
+extern double polevl ( double x, void *P, int n );
+extern double p1evl ( double x, void *P, int n );
+
+/* polyn.c */
+extern void polini ( int maxdeg );
+extern void polprt ( double a[], int na, int d );
+extern void polclr ( double *a, int n );
+extern void polmov ( double *a, int na, double *b );
+extern void polmul ( double a[], int na, double b[], int nb, double c[] );
+extern void poladd ( double a[], int na, double b[], int nb, double c[] );
+extern void polsub ( double a[], int na, double b[], int nb, double c[] );
+extern int poldiv ( double a[], int na, double b[], int nb, double c[] );
+extern void polsbt ( double a[], int na, double b[], int nb, double c[] );
+extern double poleva ( double a[], int na, double x );
+
+#endif
+
+#endif /* _CEPHES_H_ */
diff --git a/ao-tools/lib/chbevl.c b/ao-tools/lib/chbevl.c
new file mode 100644 (file)
index 0000000..2289241
--- /dev/null
@@ -0,0 +1,81 @@
+/*                                                     chbevl.c
+ *
+ *     Evaluate Chebyshev series
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * int N;
+ * double x, y, coef[N], chebevl();
+ *
+ * y = chbevl( x, coef, N );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Evaluates the series
+ *
+ *        N-1
+ *         - '
+ *  y  =   >   coef[i] T (x/2)
+ *         -            i
+ *        i=0
+ *
+ * of Chebyshev polynomials Ti at argument x/2.
+ *
+ * Coefficients are stored in reverse order, i.e. the zero
+ * order term is last in the array.  Note N is the number of
+ * coefficients, not the order.
+ *
+ * If coefficients are for the interval a to b, x must
+ * have been transformed to x -> 2(2x - b - a)/(b-a) before
+ * entering the routine.  This maps x from (a, b) to (-1, 1),
+ * over which the Chebyshev polynomials are defined.
+ *
+ * If the coefficients are for the inverted interval, in
+ * which (a, b) is mapped to (1/b, 1/a), the transformation
+ * required is x -> 2(2ab/x - b - a)/(b-a).  If b is infinity,
+ * this becomes x -> 4a/x - 1.
+ *
+ *
+ *
+ * SPEED:
+ *
+ * Taking advantage of the recurrence properties of the
+ * Chebyshev polynomials, the routine requires one more
+ * addition per loop than evaluating a nested polynomial of
+ * the same degree.
+ *
+ */
+\f/*                                                    chbevl.c        */
+
+/*
+Cephes Math Library Release 2.0:  April, 1987
+Copyright 1985, 1987 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+
+#include "cephes.h"
+
+double chbevl(double x,void* array,int n )
+{
+double b0, b1, b2, *p;
+int i;
+
+p = (double *) array;
+b0 = *p++;
+b1 = 0.0;
+i = n - 1;
+
+do
+       {
+       b2 = b1;
+       b1 = b0;
+       b0 = x * b1  -  b2  + *p++;
+       }
+while( --i );
+
+return( 0.5*(b0-b2) );
+}
diff --git a/ao-tools/lib/cmath.h b/ao-tools/lib/cmath.h
new file mode 100644 (file)
index 0000000..2751aec
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Grace - GRaphing, Advanced Computation and Exploration of data
+ *
+ * Home page: http://plasma-gate.weizmann.ac.il/Grace/
+ *
+ * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
+ * Copyright (c) 1996-2000 Grace Development Team
+ *
+ * Maintained by Evgeny Stambulchik <fnevgeny@plasma-gate.weizmann.ac.il>
+ *
+ *
+ *                           All Rights Reserved
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* cmath.h - replacement for math.h or missing in libm functions */
+
+#if defined(HAVE_MATH_H)
+#  include <math.h>
+#endif
+#if defined(HAVE_FLOAT_H)
+#  include <float.h>
+#endif
+#if defined(HAVE_IEEEFP_H)
+#  include <ieeefp.h>
+#endif
+
+#ifndef __GRACE_SOURCE_
+
+#ifndef MACHEP
+extern double MACHEP;
+#endif
+
+#ifndef UFLOWTHRESH
+extern double UFLOWTHRESH;
+#endif
+
+#ifndef MAXNUM
+extern double MAXNUM;
+#endif
+
+#endif /* __GRACE_SOURCE_ */
+
+#ifndef M_PI
+#  define M_PI  3.14159265358979323846
+#endif
+
+#ifndef M_SQRT2
+#  define M_SQRT2     1.41421356237309504880      /* sqrt(2) */
+#endif
+
+#ifndef M_SQRT1_2
+#  define M_SQRT1_2   0.70710678118654752440      /* 1/sqrt(2) */
+#endif
+
+#ifndef M_SQRT1_3
+#  define M_SQRT1_3   0.57735026918962576451      /* 1/sqrt(3) */
+#endif
+
+#ifndef HAVE_HYPOT
+#  define hypot(x, y) sqrt((x)*(x) + (y)*(y))
+#endif
+
+extern double round ( double x );
+#ifndef HAVE_RINT
+#  define rint round
+#else
+#  ifndef HAVE_RINT_DECL
+extern double rint ( double x );
+#  endif
+#endif
+
+#ifndef HAVE_CBRT_DECL
+extern double cbrt ( double x );
+#endif
+
+/* Cygnus gnuwin32 has the log2 macro */
+#ifdef log2
+#  undef log2
+#endif
+
+#ifndef HAVE_LOG2_DECL
+extern double log2 ( double x );
+#endif
+
+#ifndef HAVE_LGAMMA
+extern int sgngam;
+#  define lgamma lgam
+#  define signgam sgngam
+extern double lgam ( double x );
+#else
+#  ifndef HAVE_LGAMMA_DECL
+extern double lgamma ( double x );
+#  endif
+#  ifndef HAVE_SIGNGAM_DECL
+extern int signgam;
+#  endif
+#  define lgam lgamma
+#  define sgngam signgam
+#endif
+
+#ifndef HAVE_ACOSH_DECL
+extern double acosh ( double x );
+#endif
+
+#ifndef HAVE_ASINH_DECL
+extern double asinh ( double x );
+#endif
+
+#ifndef HAVE_ATANH_DECL
+extern double atanh ( double x );
+#endif
+
+#ifndef HAVE_ERF_DECL
+extern double erf ( double x );
+#endif
+
+#ifndef HAVE_ERFC_DECL
+extern double erfc ( double x );
+#endif
+
+#ifndef HAVE_Y0_DECL
+extern double y0 ( double x );
+#endif
+#ifndef HAVE_Y1_DECL
+extern double y1 ( double x );
+#endif
+#ifndef HAVE_YN_DECL
+extern double yn ( int n, double x );
+#endif
+#ifndef HAVE_J0_DECL
+extern double j0 ( double x );
+#endif
+#ifndef HAVE_J1_DECL
+extern double j1 ( double x );
+#endif
+#ifndef HAVE_JN_DECL
+extern double jn ( int n, double x );
+#endif
+
+/* isfinite is a macro */
+#ifdef isfinite
+#  define HAVE_ISFINITE_MACRO
+#endif
+
+#ifndef HAVE_FINITE
+#  define finite isfinite
+#  if !defined(HAVE_ISFINITE_DECL) && !defined(HAVE_ISFINITE_MACRO)
+extern int isfinite ( double x );
+#  endif
+#else
+#  ifndef HAVE_FINITE_DECL
+extern int finite ( double x );
+#  endif
+#endif
+
+#ifndef HAVE_ISNAN_DECL
+#ifdef __FreeBSD__
+#  include <sys/param.h>
+#  if __FreeBSD_version < 500100
+extern int isnan ( double x );
+#  endif
+#endif
+#else
+extern int isnan ( double x );
+#endif
diff --git a/ao-tools/lib/i0.c b/ao-tools/lib/i0.c
new file mode 100644 (file)
index 0000000..6f7b5a5
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * This file comes from the cephes math library, which was
+ * released under the GPLV2+ license as a part of the Debian labplot
+ * package (I've included the GPLV2 license reference here to make
+ * this clear) - Keith Packard <keithp@keithp.com>
+ *
+ * Cephes Math Library Release 2.0:  April, 1987
+ * Copyright 1984, 1987 by Stephen L. Moshier
+ * Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+ *
+ * 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.
+ */
+
+/*                                                     i0.c
+ *
+ *     Modified Bessel function of order zero
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, i0();
+ *
+ * y = i0( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns modified Bessel function of order zero of the
+ * argument.
+ *
+ * The function is defined as i0(x) = j0( ix ).
+ *
+ * The range is partitioned into the two intervals [0,8] and
+ * (8, infinity).  Chebyshev polynomial expansions are employed
+ * in each interval.
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    DEC       0,30         6000       8.2e-17     1.9e-17
+ *    IEEE      0,30        30000       5.8e-16     1.4e-16
+ *
+ */
+\f/*                                                    i0e.c
+ *
+ *     Modified Bessel function of order zero,
+ *     exponentially scaled
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * double x, y, i0e();
+ *
+ * y = i0e( x );
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * Returns exponentially scaled modified Bessel function
+ * of order zero of the argument.
+ *
+ * The function is defined as i0e(x) = exp(-|x|) j0( ix ).
+ *
+ *
+ *
+ * ACCURACY:
+ *
+ *                      Relative error:
+ * arithmetic   domain     # trials      peak         rms
+ *    IEEE      0,30        30000       5.4e-16     1.2e-16
+ * See i0().
+ *
+ */
+\f
+/*                                                     i0.c            */
+
+
+/*
+Cephes Math Library Release 2.0:  April, 1987
+Copyright 1984, 1987 by Stephen L. Moshier
+Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+*/
+
+#include <math.h>
+#include "mconf.h"
+#include "cephes.h"
+
+/* Chebyshev coefficients for exp(-x) I0(x)
+ * in the interval [0,8].
+ *
+ * lim(x->0){ exp(-x) I0(x) } = 1.
+ */
+
+#ifdef UNK
+static double A[] =
+{
+-4.41534164647933937950E-18,
+ 3.33079451882223809783E-17,
+-2.43127984654795469359E-16,
+ 1.71539128555513303061E-15,
+-1.16853328779934516808E-14,
+ 7.67618549860493561688E-14,
+-4.85644678311192946090E-13,
+ 2.95505266312963983461E-12,
+-1.72682629144155570723E-11,
+ 9.67580903537323691224E-11,
+-5.18979560163526290666E-10,
+ 2.65982372468238665035E-9,
+-1.30002500998624804212E-8,
+ 6.04699502254191894932E-8,
+-2.67079385394061173391E-7,
+ 1.11738753912010371815E-6,
+-4.41673835845875056359E-6,
+ 1.64484480707288970893E-5,
+-5.75419501008210370398E-5,
+ 1.88502885095841655729E-4,
+-5.76375574538582365885E-4,
+ 1.63947561694133579842E-3,
+-4.32430999505057594430E-3,
+ 1.05464603945949983183E-2,
+-2.37374148058994688156E-2,
+ 4.93052842396707084878E-2,
+-9.49010970480476444210E-2,
+ 1.71620901522208775349E-1,
+-3.04682672343198398683E-1,
+ 6.76795274409476084995E-1
+};
+#endif
+
+#ifdef DEC
+static unsigned short A[] = {
+0121642,0162671,0004646,0103567,
+0022431,0115424,0135755,0026104,
+0123214,0023533,0110365,0156635,
+0023767,0033304,0117662,0172716,
+0124522,0100426,0012277,0157531,
+0025254,0155062,0054461,0030465,
+0126010,0131143,0013560,0153604,
+0026517,0170577,0006336,0114437,
+0127227,0162253,0152243,0052734,
+0027724,0142766,0061641,0160200,
+0130416,0123760,0116564,0125262,
+0031066,0144035,0021246,0054641,
+0131537,0053664,0060131,0102530,
+0032201,0155664,0165153,0020652,
+0132617,0061434,0074423,0176145,
+0033225,0174444,0136147,0122542,
+0133624,0031576,0056453,0020470,
+0034211,0175305,0172321,0041314,
+0134561,0054462,0147040,0165315,
+0035105,0124333,0120203,0162532,
+0135427,0013750,0174257,0055221,
+0035726,0161654,0050220,0100162,
+0136215,0131361,0000325,0041110,
+0036454,0145417,0117357,0017352,
+0136702,0072367,0104415,0133574,
+0037111,0172126,0072505,0014544,
+0137302,0055601,0120550,0033523,
+0037457,0136543,0136544,0043002,
+0137633,0177536,0001276,0066150,
+0040055,0041164,0100655,0010521
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short A[] = {
+0xd0ef,0x2134,0x5cb7,0xbc54,
+0xa589,0x977d,0x3362,0x3c83,
+0xbbb4,0x721e,0x84eb,0xbcb1,
+0x5eba,0x93f6,0xe6d8,0x3cde,
+0xfbeb,0xc297,0x5022,0xbd0a,
+0x2627,0x4b26,0x9b46,0x3d35,
+0x1af0,0x62ee,0x164c,0xbd61,
+0xd324,0xe19b,0xfe2f,0x3d89,
+0x6abc,0x7a94,0xfc95,0xbdb2,
+0x3c10,0xcc74,0x98be,0x3dda,
+0x9556,0x13ae,0xd4fe,0xbe01,
+0xcb34,0xa454,0xd903,0x3e26,
+0x30ab,0x8c0b,0xeaf6,0xbe4b,
+0x6435,0x9d4d,0x3b76,0x3e70,
+0x7f8d,0x8f22,0xec63,0xbe91,
+0xf4ac,0x978c,0xbf24,0x3eb2,
+0x6427,0xcba5,0x866f,0xbed2,
+0x2859,0xbe9a,0x3f58,0x3ef1,
+0x1d5a,0x59c4,0x2b26,0xbf0e,
+0x7cab,0x7410,0xb51b,0x3f28,
+0xeb52,0x1f15,0xe2fd,0xbf42,
+0x100e,0x8a12,0xdc75,0x3f5a,
+0xa849,0x201a,0xb65e,0xbf71,
+0xe3dd,0xf3dd,0x9961,0x3f85,
+0xb6f0,0xf121,0x4e9e,0xbf98,
+0xa32d,0xcea8,0x3e8a,0x3fa9,
+0x06ea,0x342d,0x4b70,0xbfb8,
+0x88c0,0x77ac,0xf7ac,0x3fc5,
+0xcd8d,0xc057,0x7feb,0xbfd3,
+0xa22a,0x9035,0xa84e,0x3fe5,
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short A[] = {
+0xbc54,0x5cb7,0x2134,0xd0ef,
+0x3c83,0x3362,0x977d,0xa589,
+0xbcb1,0x84eb,0x721e,0xbbb4,
+0x3cde,0xe6d8,0x93f6,0x5eba,
+0xbd0a,0x5022,0xc297,0xfbeb,
+0x3d35,0x9b46,0x4b26,0x2627,
+0xbd61,0x164c,0x62ee,0x1af0,
+0x3d89,0xfe2f,0xe19b,0xd324,
+0xbdb2,0xfc95,0x7a94,0x6abc,
+0x3dda,0x98be,0xcc74,0x3c10,
+0xbe01,0xd4fe,0x13ae,0x9556,
+0x3e26,0xd903,0xa454,0xcb34,
+0xbe4b,0xeaf6,0x8c0b,0x30ab,
+0x3e70,0x3b76,0x9d4d,0x6435,
+0xbe91,0xec63,0x8f22,0x7f8d,
+0x3eb2,0xbf24,0x978c,0xf4ac,
+0xbed2,0x866f,0xcba5,0x6427,
+0x3ef1,0x3f58,0xbe9a,0x2859,
+0xbf0e,0x2b26,0x59c4,0x1d5a,
+0x3f28,0xb51b,0x7410,0x7cab,
+0xbf42,0xe2fd,0x1f15,0xeb52,
+0x3f5a,0xdc75,0x8a12,0x100e,
+0xbf71,0xb65e,0x201a,0xa849,
+0x3f85,0x9961,0xf3dd,0xe3dd,
+0xbf98,0x4e9e,0xf121,0xb6f0,
+0x3fa9,0x3e8a,0xcea8,0xa32d,
+0xbfb8,0x4b70,0x342d,0x06ea,
+0x3fc5,0xf7ac,0x77ac,0x88c0,
+0xbfd3,0x7feb,0xc057,0xcd8d,
+0x3fe5,0xa84e,0x9035,0xa22a
+};
+#endif
+
+
+/* Chebyshev coefficients for exp(-x) sqrt(x) I0(x)
+ * in the inverted interval [8,infinity].
+ *
+ * lim(x->inf){ exp(-x) sqrt(x) I0(x) } = 1/sqrt(2pi).
+ */
+
+#ifdef UNK
+static double B[] =
+{
+-7.23318048787475395456E-18,
+-4.83050448594418207126E-18,
+ 4.46562142029675999901E-17,
+ 3.46122286769746109310E-17,
+-2.82762398051658348494E-16,
+-3.42548561967721913462E-16,
+ 1.77256013305652638360E-15,
+ 3.81168066935262242075E-15,
+-9.55484669882830764870E-15,
+-4.15056934728722208663E-14,
+ 1.54008621752140982691E-14,
+ 3.85277838274214270114E-13,
+ 7.18012445138366623367E-13,
+-1.79417853150680611778E-12,
+-1.32158118404477131188E-11,
+-3.14991652796324136454E-11,
+ 1.18891471078464383424E-11,
+ 4.94060238822496958910E-10,
+ 3.39623202570838634515E-9,
+ 2.26666899049817806459E-8,
+ 2.04891858946906374183E-7,
+ 2.89137052083475648297E-6,
+ 6.88975834691682398426E-5,
+ 3.36911647825569408990E-3,
+ 8.04490411014108831608E-1
+};
+#endif
+
+#ifdef DEC
+static unsigned short B[] = {
+0122005,0066672,0123124,0054311,
+0121662,0033323,0030214,0104602,
+0022515,0170300,0113314,0020413,
+0022437,0117350,0035402,0007146,
+0123243,0000135,0057220,0177435,
+0123305,0073476,0144106,0170702,
+0023777,0071755,0017527,0154373,
+0024211,0052214,0102247,0033270,
+0124454,0017763,0171453,0012322,
+0125072,0166316,0075505,0154616,
+0024612,0133770,0065376,0025045,
+0025730,0162143,0056036,0001632,
+0026112,0015077,0150464,0063542,
+0126374,0101030,0014274,0065457,
+0127150,0077271,0125763,0157617,
+0127412,0104350,0040713,0120445,
+0027121,0023765,0057500,0001165,
+0030407,0147146,0003643,0075644,
+0031151,0061445,0044422,0156065,
+0031702,0132224,0003266,0125551,
+0032534,0000076,0147153,0005555,
+0033502,0004536,0004016,0026055,
+0034620,0076433,0142314,0171215,
+0036134,0146145,0013454,0101104,
+0040115,0171425,0062500,0047133
+};
+#endif
+
+#ifdef IBMPC
+static unsigned short B[] = {
+0x8b19,0x54ca,0xadb7,0xbc60,
+0x9130,0x6611,0x46da,0xbc56,
+0x8421,0x12d9,0xbe18,0x3c89,
+0x41cd,0x0760,0xf3dd,0x3c83,
+0x1fe4,0xabd2,0x600b,0xbcb4,
+0xde38,0xd908,0xaee7,0xbcb8,
+0xfb1f,0xa3ea,0xee7d,0x3cdf,
+0xe6d7,0x9094,0x2a91,0x3cf1,
+0x629a,0x7e65,0x83fe,0xbd05,
+0xbb32,0xcf68,0x5d99,0xbd27,
+0xc545,0x0d5f,0x56ff,0x3d11,
+0xc073,0x6b83,0x1c8c,0x3d5b,
+0x8cec,0xfa26,0x4347,0x3d69,
+0x8d66,0x0317,0x9043,0xbd7f,
+0x7bf2,0x357e,0x0fd7,0xbdad,
+0x7425,0x0839,0x511d,0xbdc1,
+0x004f,0xabe8,0x24fe,0x3daa,
+0x6f75,0xc0f4,0xf9cc,0x3e00,
+0x5b87,0xa922,0x2c64,0x3e2d,
+0xd56d,0x80d6,0x5692,0x3e58,
+0x616e,0xd9cd,0x8007,0x3e8b,
+0xc586,0xc101,0x412b,0x3ec8,
+0x9e52,0x7899,0x0fa3,0x3f12,
+0x9049,0xa2e5,0x998c,0x3f6b,
+0x09cb,0xaca8,0xbe62,0x3fe9
+};
+#endif
+
+#ifdef MIEEE
+static unsigned short B[] = {
+0xbc60,0xadb7,0x54ca,0x8b19,
+0xbc56,0x46da,0x6611,0x9130,
+0x3c89,0xbe18,0x12d9,0x8421,
+0x3c83,0xf3dd,0x0760,0x41cd,
+0xbcb4,0x600b,0xabd2,0x1fe4,
+0xbcb8,0xaee7,0xd908,0xde38,
+0x3cdf,0xee7d,0xa3ea,0xfb1f,
+0x3cf1,0x2a91,0x9094,0xe6d7,
+0xbd05,0x83fe,0x7e65,0x629a,
+0xbd27,0x5d99,0xcf68,0xbb32,
+0x3d11,0x56ff,0x0d5f,0xc545,
+0x3d5b,0x1c8c,0x6b83,0xc073,
+0x3d69,0x4347,0xfa26,0x8cec,
+0xbd7f,0x9043,0x0317,0x8d66,
+0xbdad,0x0fd7,0x357e,0x7bf2,
+0xbdc1,0x511d,0x0839,0x7425,
+0x3daa,0x24fe,0xabe8,0x004f,
+0x3e00,0xf9cc,0xc0f4,0x6f75,
+0x3e2d,0x2c64,0xa922,0x5b87,
+0x3e58,0x5692,0x80d6,0xd56d,
+0x3e8b,0x8007,0xd9cd,0x616e,
+0x3ec8,0x412b,0xc101,0xc586,
+0x3f12,0x0fa3,0x7899,0x9e52,
+0x3f6b,0x998c,0xa2e5,0x9049,
+0x3fe9,0xbe62,0xaca8,0x09cb
+};
+#endif
+
+double i0(double x)
+{
+double y;
+
+if( x < 0 )
+       x = -x;
+if( x <= 8.0 )
+       {
+       y = (x/2.0) - 2.0;
+       return( exp(x) * chbevl( y, A, 30 ) );
+       }
+
+return(  exp(x) * chbevl( 32.0/x - 2.0, B, 25 ) / sqrt(x) );
+
+}
+
+
+
+
+double i0e(double x )
+{
+double y;
+
+if( x < 0 )
+       x = -x;
+if( x <= 8.0 )
+       {
+       y = (x/2.0) - 2.0;
+       return( chbevl( y, A, 30 ) );
+       }
+
+return(  chbevl( 32.0/x - 2.0, B, 25 ) / sqrt(x) );
+
+}
diff --git a/ao-tools/lib/mconf.h b/ao-tools/lib/mconf.h
new file mode 100644 (file)
index 0000000..af1ebb5
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * This file comes from the cephes math library, which was
+ * released under the GPLV2+ license as a part of the Debian labplot
+ * package (I've included the GPLV2 license reference here to make
+ * this clear) - Keith Packard <keithp@keithp.com>
+ *
+ * Cephes Math Library Release 2.0:  April, 1987
+ * Copyright 1984, 1987 by Stephen L. Moshier
+ * Direct inquiries to 30 Frost Street, Cambridge, MA 02140
+ *
+ * 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.
+ */
+/*                                                     mconf.h
+ *
+ *     Common include file for math routines
+ *
+ *
+ *
+ * SYNOPSIS:
+ *
+ * #include "mconf.h"
+ *
+ *
+ *
+ * DESCRIPTION:
+ *
+ * This file contains definitions for error codes that are
+ * passed to the common error handling routine mtherr()
+ * (which see).
+ *
+ * The file also includes a conditional assembly definition
+ * for the type of computer arithmetic (IEEE, DEC, Motorola
+ * IEEE, or UNKnown).
+ *
+ * For Digital Equipment PDP-11 and VAX computers, certain
+ * IBM systems, and others that use numbers with a 56-bit
+ * significand, the symbol DEC should be defined.  In this
+ * mode, most floating point constants are given as arrays
+ * of octal integers to eliminate decimal to binary conversion
+ * errors that might be introduced by the compiler.
+ *
+ * For little-endian computers, such as IBM PC, that follow the
+ * IEEE Standard for Binary Floating Point Arithmetic (ANSI/IEEE
+ * Std 754-1985), the symbol IBMPC should be defined.  These
+ * numbers have 53-bit significands.  In this mode, constants
+ * are provided as arrays of hexadecimal 16 bit integers.
+ *
+ * Big-endian IEEE format is denoted MIEEE.  On some RISC
+ * systems such as Sun SPARC, double precision constants
+ * must be stored on 8-byte address boundaries.  Since integer
+ * arrays may be aligned differently, the MIEEE configuration
+ * may fail on such machines.
+ *
+ * To accommodate other types of computer arithmetic, all
+ * constants are also provided in a normal decimal radix
+ * which one can hope are correctly converted to a suitable
+ * format by the available C language compiler.  To invoke
+ * this mode, define the symbol UNK.
+ *
+ * An important difference among these modes is a predefined
+ * set of machine arithmetic constants for each.  The numbers
+ * MACHEP (the machine roundoff error), MAXNUM (largest number
+ * represented), and several other parameters are preset by
+ * the configuration symbol.  Check the file const.c to
+ * ensure that these values are correct for your computer.
+ *
+ * Configurations NANS, INFINITIES, MINUSZERO, and DENORMAL
+ * may fail on many systems.  Verify that they are supposed
+ * to work on your computer.
+ */
+\f
+/*
+Cephes Math Library Release 2.3:  June, 1995
+Copyright 1984, 1987, 1989, 1995 by Stephen L. Moshier
+
+Adjusted for use with ACE/gr by Evgeny Stambulchik, October 1997
+*/
+
+#define __GRACE_SOURCE_
+
+#include "cmath.h"
+
+/* Type of computer arithmetic */
+/* In ACE/gr, defined as a compiler directive - no need to define here */
+
+/* PDP-11, Pro350, VAX:
+ */
+#if defined(HAVE_DEC_FPU)
+#  define DEC 1
+#endif
+
+/* Intel IEEE, low order words come first:
+ */
+#if defined(HAVE_LIEEE_FPU)
+#  define IBMPC 1
+#endif
+
+/* Motorola IEEE, high order words come first
+ * (Sun 680x0 workstation):
+ */
+#if defined(HAVE_BIEEE_FPU)
+#  define MIEEE 1
+#endif
+
+/* UNKnown arithmetic, invokes coefficients given in
+ * normal decimal format.  Beware of range boundary
+ * problems (MACHEP, MAXLOG, etc. in const.c) and
+ * roundoff problems in pow.c:
+ * (Sun SPARCstation)
+ */
+
+#if (!defined (DEC) && !defined (IBMPC) && !defined (MIEEE))
+#  define UNK 1
+#endif
+
+/* Define this `volatile' if your compiler thinks
+ * that floating point arithmetic obeys the associative
+ * and distributive laws.  It will defeat some optimizations
+ * (but probably not enough of them).
+ *
+ * #define VOLATILE volatile
+ */
+
+#ifndef VOLATILE
+#  define VOLATILE
+#endif
+
+#ifdef PI
+#  undef PI
+#endif
+
+#ifdef NAN
+#  undef NAN
+#endif
+
+#ifdef INFINITY
+#  undef INFINITY
+#endif
+
+/* Constant definitions for math error conditions
+ */
+
+#if defined(DOMAIN)
+#  undef DOMAIN
+#endif
+#define DOMAIN         1       /* argument domain error */
+
+#if defined(SING)
+#  undef SING
+#endif
+#define SING           2       /* argument singularity */
+
+#if defined(OVERFLOW)
+#  undef OVERFLOW
+#endif
+#define OVERFLOW       3       /* overflow range error */
+
+#if defined(UNDERFLOW)
+#  undef UNDERFLOW
+#endif
+#define UNDERFLOW      4       /* underflow range error */
+
+#if defined(TLOSS)
+#  undef TLOSS
+#endif
+#define TLOSS          5       /* total loss of precision */
+
+#if defined(PLOSS)
+#  undef PLOSS
+#endif
+#define PLOSS          6       /* partial loss of precision */
+
+#if defined(EDOM)
+#  undef EDOM
+#endif
+#define EDOM           33
+
+#if defined(ERANGE)
+#  undef ERANGE
+#endif
+#define ERANGE         34
+
+#if !defined (UNK)
+  /* Define to support tiny denormal numbers, else undefine. */
+#  define DENORMAL 1
+
+  /* Define to ask for infinity support, else undefine. */
+#  define INFINITIES 1
+
+  /* Define to ask for support of numbers that are Not-a-Number,
+   else undefine.  This may automatically define INFINITIES in some files. */
+#  define NANS 1
+
+  /* Define to distinguish between -0.0 and +0.0.  */
+#  define MINUSZERO 1
+#endif
+
+/* Define 1 for ANSI C atan2() function
+   See atan.c and clog.c. */
+#define ANSIC 1
index a32c8bec770c676e78c93f9f923d9a847de07504..65b7280328c19de28038c3d7c9e987c3feeffb4d 100644 (file)
@@ -1,5 +1,7 @@
 # reset
 C D R
-C D R
-C D R
+C D .
+C D .
+C D .
+C D .
 C D R
diff --git a/ao-view/.gitignore b/ao-view/.gitignore
deleted file mode 100644 (file)
index 24fbc59..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-*.o
-aoview
-aoview_glade.h
-aoview_flite
diff --git a/ao-view/Makefile.am b/ao-view/Makefile.am
deleted file mode 100644 (file)
index 17661c8..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-VERSION=$(shell git describe)
-
-AM_CFLAGS=$(GNOME_CFLAGS) $(ALSA_CFLAGS) -I$(top_srcdir)/src -DAOVIEW_VERSION=\"$(VERSION)\" @FLITE_INCS@
-
-bin_PROGRAMS=ao-view
-
-ao_view_LDADD=$(GNOME_LIBS) $(FLITE_LIBS) $(ALSA_LIBS)
-
-ao_view_SOURCES = \
-       aoview_main.c \
-       aoview_dev.c \
-       aoview_dev_dialog.c \
-       aoview_serial.c \
-       aoview_monitor.c \
-       aoview_state.c \
-       aoview_convert.c \
-       aoview_log.c \
-       aoview_table.c \
-       aoview_util.c \
-       aoview_file.c \
-       aoview_eeprom.c \
-       aoview_voice.c \
-       aoview_replay.c \
-       aoview_label.c \
-       aoview_flite.c \
-       aoview.h
-
-BUILT_SOURCES = aoview_glade.h
-
-CLEANFILES = aoview_glade.h
-
-man_MANS=ao-view.1
-
-aoview_glade.h: aoview.glade
-       sed -e 's/"/\\"/g' -e 's/^/"/' -e 's/$$/"/' $< > $@
diff --git a/ao-view/ao-view.1 b/ao-view/ao-view.1
deleted file mode 100644 (file)
index 99834c4..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-.\"
-.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
-.\"
-.\" This program is free software; you can redistribute it and/or modify
-.\" it under the terms of the GNU General Public License as published by
-.\" the Free Software Foundation; either version 2 of the License, or
-.\" (at your option) any later version.
-.\"
-.\" This program is distributed in the hope that it will be useful, but
-.\" WITHOUT ANY WARRANTY; without even the implied warranty of
-.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-.\" General Public License for more details.
-.\"
-.\" You should have received a copy of the GNU General Public License along
-.\" with this program; if not, write to the Free Software Foundation, Inc.,
-.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
-.\"
-.\"
-.TH AO-VIEW 1 "ao-view" ""
-.SH NAME
-ao-view \- Rocket flight monitor
-.SH SYNOPSIS
-.B "ao-view"
-[\--tty \fItty-device\fP]
-.SH DESCRIPTION
-.I ao-view
-connects to a TeleDongle or TeleMetrum device through a USB serial device.
-It provides a user interface to monitor, record and review rocket flight data.
-.SH OPTIONS
-The usual Gtk+ command line options can be used, along with
-.IP "\--tty"
-This selects a target device to connect at startup time to.
-The target device may also be selected through the user interface.
-.SH USAGE
-When connected to a TeleDongle device, ao-view turns on the radio
-receiver and listens for telemetry packets. It displays the received
-telemetry data, and reports flight status via voice synthesis. All
-received telemetry information is recorded to a file.
-.P
-When connected to a TeleMetrum device, ao-view downloads the eeprom
-data and stores it in a file.
-.SH FILES
-All data log files are recorded into a user-specified directory
-(default ~/AltOS). Files are named using the current date, the serial
-number of the reporting device, the flight number recorded in the data
-and either '.telem' for telemetry data or '.eeprom' for eeprom data.
-.SH "SEE ALSO"
-ao-load(1), ao-eeprom(1)
-.SH AUTHOR
-Keith Packard
diff --git a/ao-view/aoview.glade b/ao-view/aoview.glade
deleted file mode 100644 (file)
index 3481a77..0000000
+++ /dev/null
@@ -1,733 +0,0 @@
-<?xml version="1.0"?>
-<glade-interface>
-  <!-- interface-requires gtk+ 2.16 -->
-  <!-- interface-naming-policy project-wide -->
-  <widget class="GtkWindow" id="aoview">
-    <property name="width_request">550</property>
-    <property name="height_request">700</property>
-    <property name="visible">True</property>
-    <property name="title" translatable="yes">AltOS View</property>
-    <child>
-      <widget class="GtkVBox" id="vbox1">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <child>
-          <widget class="GtkMenuBar" id="menubar1">
-            <property name="visible">True</property>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem1">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_File</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu1">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem1">
-                        <property name="label">gtk-new</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem2">
-                        <property name="label">gtk-open</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem3">
-                        <property name="label">gtk-save</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem4">
-                        <property name="label">gtk-save-as</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="separatormenuitem1">
-                        <property name="visible">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem5">
-                        <property name="label">gtk-quit</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <signal name="activate" handler="gtk_main_quit"/>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem2">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_Edit</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu2">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem6">
-                        <property name="label">gtk-cut</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem7">
-                        <property name="label">gtk-copy</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem8">
-                        <property name="label">gtk-paste</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem9">
-                        <property name="label">gtk-delete</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem3">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_Device</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu4">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="ao_connect">
-                        <property name="label" translatable="yes">_Connect to device</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">False</property>
-                        <signal name="activate_item" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
-                        <signal name="activate" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
-                        <child internal-child="image">
-                          <widget class="GtkImage" id="image1">
-                            <property name="visible">True</property>
-                            <property name="stock">gtk-connect</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="ao_disconnect">
-                        <property name="label" translatable="yes">_Disconnect</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">False</property>
-                        <child internal-child="image">
-                          <widget class="GtkImage" id="image2">
-                            <property name="visible">True</property>
-                            <property name="stock">gtk-disconnect</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkSeparatorMenuItem" id="seperator">
-                        <property name="visible">True</property>
-                        <property name="sensitive">False</property>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="ao_savelog">
-                        <property name="label" translatable="yes">_Save EEPROM data</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">False</property>
-                        <signal name="activate_item" handler="gtk_widget_show" object="device_connect_dialog" after="yes"/>
-                        <signal name="activate" handler="gtk_widget_show" object="device_connect_dialog"/>
-                        <child internal-child="image">
-                          <widget class="GtkImage" id="image5">
-                            <property name="visible">True</property>
-                            <property name="stock">gtk-save</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="ao_replay">
-                        <property name="label" translatable="yes">_Replay</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">False</property>
-                        <signal name="activate_item" handler="gtk_widget_show" object="ao_replay_dialog" after="yes"/>
-                        <signal name="activate" handler="gtk_widget_show" object="ao_replay_dialog"/>
-                        <child internal-child="image">
-                          <widget class="GtkImage" id="image6">
-                            <property name="visible">True</property>
-                            <property name="stock">gtk-media-play</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem5">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_Log</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu5">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="log_new">
-                        <property name="label" translatable="yes">_New log</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">False</property>
-                        <child internal-child="image">
-                          <widget class="GtkImage" id="image3">
-                            <property name="visible">True</property>
-                            <property name="stock">gtk-new</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="file_configure">
-                        <property name="label" translatable="yes">_Configure Log</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">False</property>
-                        <signal name="activate" handler="gtk_widget_show" object="file_chooser_dialog" after="yes"/>
-                        <child internal-child="image">
-                          <widget class="GtkImage" id="image4">
-                            <property name="visible">True</property>
-                            <property name="stock">gtk-preferences</property>
-                          </widget>
-                        </child>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem6">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_Voice</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu6">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkCheckMenuItem" id="voice_enable">
-                        <property name="visible">True</property>
-                        <property name="label" translatable="yes">Enable _Voice</property>
-                        <property name="use_underline">True</property>
-                        <property name="active">True</property>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkMenuItem" id="menuitem4">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">_Help</property>
-                <property name="use_underline">True</property>
-                <child>
-                  <widget class="GtkMenu" id="menu3">
-                    <property name="visible">True</property>
-                    <child>
-                      <widget class="GtkImageMenuItem" id="imagemenuitem10">
-                        <property name="label">gtk-about</property>
-                        <property name="visible">True</property>
-                        <property name="use_underline">True</property>
-                        <property name="use_stock">True</property>
-                        <signal name="activate" handler="gtk_widget_show" object="about_dialog" after="yes"/>
-                      </widget>
-                    </child>
-                  </widget>
-                </child>
-              </widget>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkTable" id="table1">
-            <property name="visible">True</property>
-            <property name="n_rows">2</property>
-            <property name="n_columns">4</property>
-            <property name="row_spacing">3</property>
-            <property name="homogeneous">True</property>
-            <child>
-              <widget class="GtkLabel" id="height_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Height (m)</property>
-                <property name="justify">center</property>
-              </widget>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="state_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">State</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="rssi_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">RSSI (dBm)</property>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="height_value">
-                <property name="visible">True</property>
-                <property name="ypad">2</property>
-                <property name="label" translatable="yes">0</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="state_value">
-                <property name="visible">True</property>
-                <property name="ypad">2</property>
-                <property name="label" translatable="yes">pad</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">1</property>
-                <property name="right_attach">2</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="rssi_value">
-                <property name="visible">True</property>
-                <property name="ypad">2</property>
-                <property name="label" translatable="yes">-50</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">2</property>
-                <property name="right_attach">3</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="speed_label">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">Speed (m/s)</property>
-              </widget>
-              <packing>
-                <property name="left_attach">3</property>
-                <property name="right_attach">4</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkLabel" id="speed_value">
-                <property name="visible">True</property>
-                <property name="label" translatable="yes">0</property>
-                <property name="selectable">True</property>
-              </widget>
-              <packing>
-                <property name="left_attach">3</property>
-                <property name="right_attach">4</property>
-                <property name="top_attach">1</property>
-                <property name="bottom_attach">2</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child>
-          <widget class="GtkHBox" id="hbox1">
-            <property name="visible">True</property>
-            <child>
-              <widget class="GtkTreeView" id="dataview_0">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="show_expanders">False</property>
-                <property name="enable_grid_lines">both</property>
-              </widget>
-              <packing>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkTreeView" id="dataview_1">
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="show_expanders">False</property>
-                <property name="enable_grid_lines">both</property>
-              </widget>
-              <packing>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="position">2</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkDialog" id="device_connect_dialog">
-    <property name="border_width">5</property>
-    <property name="type_hint">normal</property>
-    <property name="has_separator">False</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox1">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child>
-          <widget class="GtkTreeView" id="dev_list">
-            <property name="width_request">300</property>
-            <property name="height_request">100</property>
-            <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="headers_clickable">False</property>
-            <property name="rules_hint">True</property>
-            <property name="search_column">0</property>
-            <property name="show_expanders">False</property>
-            <property name="level_indentation">1</property>
-            <property name="enable_grid_lines">both</property>
-            <property name="enable_tree_lines">True</property>
-          </widget>
-          <packing>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area1">
-            <property name="visible">True</property>
-            <property name="layout_style">end</property>
-            <child>
-              <widget class="GtkButton" id="cancel_button">
-                <property name="label" translatable="yes">gtk-cancel</property>
-                <property name="response_id">1</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_underline">True</property>
-                <property name="use_stock">True</property>
-                <signal name="clicked" handler="gtk_widget_hide" object="device_connect_dialog" after="yes"/>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="connect_button">
-                <property name="label" translatable="yes">gtk-connect</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="has_default">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_stock">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkFileChooserDialog" id="file_chooser_dialog">
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">Configure Log Directory</property>
-    <property name="type_hint">dialog</property>
-    <property name="has_separator">False</property>
-    <property name="action">select-folder</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox2">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area2">
-            <property name="visible">True</property>
-            <property name="layout_style">end</property>
-            <child>
-              <widget class="GtkButton" id="file_configure_cancel">
-                <property name="label" translatable="yes">gtk-cancel</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_stock">True</property>
-                <signal name="clicked" handler="gtk_widget_hide" object="file_chooser_dialog"/>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="file_configure_ok">
-                <property name="label" translatable="yes">gtk-ok</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="can_default">True</property>
-                <property name="has_default">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_stock">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkMessageDialog" id="file_fail_dialog">
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">Failed to create log</property>
-    <property name="type_hint">normal</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="transient_for">aoview</property>
-    <property name="message_type">error</property>
-    <property name="buttons">close</property>
-    <property name="text">Cannot create log file</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox4">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area4">
-            <property name="visible">True</property>
-            <property name="layout_style">end</property>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkMessageDialog" id="dev_open_fail_dialog">
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">Failed to open device</property>
-    <property name="type_hint">normal</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="transient_for">aoview</property>
-    <property name="message_type">error</property>
-    <property name="buttons">close</property>
-    <property name="text">Cannot open device</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox6">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area6">
-            <property name="visible">True</property>
-            <property name="layout_style">end</property>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkAboutDialog" id="about_dialog">
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">About AoView</property>
-    <property name="resizable">False</property>
-    <property name="type_hint">normal</property>
-    <property name="transient_for">aoview</property>
-    <property name="has_separator">False</property>
-    <property name="program_name">AoView</property>
-    <property name="copyright" translatable="yes">Copyright &#xA9; 2009 Keith Packard</property>
-    <property name="comments" translatable="yes">AltOS data capture and display.</property>
-    <property name="website">http://altusmetrum.org</property>
-    <property name="license" translatable="yes">AoView is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 of the License.
-
-AoView is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with AoView; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</property>
-    <property name="authors">Keith Packard &lt;keithp@keithp.com&gt;</property>
-    <property name="wrap_license">True</property>
-    <signal name="close" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
-    <signal name="response" handler="gtk_widget_hide" object="about_dialog" after="yes"/>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox7">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area7">
-            <property name="visible">True</property>
-            <property name="layout_style">end</property>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkMessageDialog" id="ao_save_done">
-    <property name="border_width">5</property>
-    <property name="title" translatable="yes">EEPROM save complete</property>
-    <property name="type_hint">normal</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="transient_for">aoview</property>
-    <property name="buttons">close</property>
-    <property name="text">Saving EEPROM data as</property>
-    <property name="secondary_text">&lt;filename&gt;</property>
-    <signal name="close" handler="gtk_widget_hide" object="ao_save_done"/>
-    <signal name="response" handler="gtk_widget_hide" object="ao_save_done"/>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox11">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area11">
-            <property name="visible">True</property>
-            <property name="layout_style">end</property>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-  <widget class="GtkFileChooserDialog" id="ao_replay_dialog">
-    <property name="border_width">5</property>
-    <property name="destroy_with_parent">True</property>
-    <property name="type_hint">dialog</property>
-    <property name="skip_taskbar_hint">True</property>
-    <property name="transient_for">aoview</property>
-    <property name="has_separator">False</property>
-    <child internal-child="vbox">
-      <widget class="GtkVBox" id="dialog-vbox10">
-        <property name="visible">True</property>
-        <property name="orientation">vertical</property>
-        <property name="spacing">2</property>
-        <child internal-child="action_area">
-          <widget class="GtkHButtonBox" id="dialog-action_area10">
-            <property name="visible">True</property>
-            <property name="layout_style">end</property>
-            <child>
-              <widget class="GtkButton" id="ao_replay_cancel">
-                <property name="label" translatable="yes">gtk-cancel</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_stock">True</property>
-                <signal name="clicked" handler="gtk_widget_hide" object="ao_replay_dialog"/>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">0</property>
-              </packing>
-            </child>
-            <child>
-              <widget class="GtkButton" id="ao_replay_ok">
-                <property name="label" translatable="yes">gtk-ok</property>
-                <property name="visible">True</property>
-                <property name="can_focus">True</property>
-                <property name="receives_default">True</property>
-                <property name="use_stock">True</property>
-              </widget>
-              <packing>
-                <property name="expand">False</property>
-                <property name="fill">False</property>
-                <property name="position">1</property>
-              </packing>
-            </child>
-          </widget>
-          <packing>
-            <property name="expand">False</property>
-            <property name="pack_type">end</property>
-            <property name="position">0</property>
-          </packing>
-        </child>
-      </widget>
-    </child>
-  </widget>
-</glade-interface>
diff --git a/ao-view/aoview.h b/ao-view/aoview.h
deleted file mode 100644 (file)
index e8334e5..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef _AOVIEW_H_
-#define _AOVIEW_H_
-
-#define _GNU_SOURCE
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <err.h>
-#include <errno.h>
-#include <getopt.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <assert.h>
-#include <math.h>
-
-#include <gtk/gtk.h>
-#include <glade/glade.h>
-#include <gconf/gconf-client.h>
-
-struct usbdev {
-       char    *sys;
-       char    *tty;
-       char    *manufacturer;
-       char    *product;
-       char    *serial;
-       int     idProduct;
-       int     idVendor;
-};
-
-struct aogps_time {
-       int hour;
-       int minute;
-       int second;
-};
-
-struct aogps {
-       int     nsat;
-       int     gps_locked;
-       int     gps_connected;
-       struct aogps_time gps_time;
-       double  lat;            /* degrees (+N -S) */
-       double  lon;            /* degrees (+E -W) */
-       int     alt;            /* m */
-
-       int     gps_extended;   /* has extra data */
-       double  ground_speed;   /* m/s */
-       int     course;         /* degrees */
-       double  climb_rate;     /* m/s */
-       double  hdop;           /* unitless? */
-       int     h_error;        /* m */
-       int     v_error;        /* m */
-};
-
-struct aodata {
-       char    callsign[16];
-       int     serial;
-       int     rssi;
-       char    state[16];
-       int     tick;
-       int     accel;
-       int     pres;
-       int     temp;
-       int     batt;
-       int     drogue;
-       int     main;
-       int     flight_accel;
-       int     ground_accel;
-       int     flight_vel;
-       int     flight_pres;
-       int     ground_pres;
-       struct aogps    gps;
-};
-
-struct aostate {
-       struct aodata   data;
-
-       /* derived data */
-
-       struct aodata   prev_data;
-
-       double          report_time;
-
-       gboolean        ascent; /* going up? */
-
-       int     ground_altitude;
-       int     height;
-       double  speed;
-       double  acceleration;
-       double  battery;
-       double  temperature;
-       double  main_sense;
-       double  drogue_sense;
-       double  baro_speed;
-
-       int     max_height;
-       double  max_acceleration;
-       double  max_speed;
-
-       struct aogps    gps;
-
-       int     gps_valid;
-       double  pad_lat;
-       double  pad_lon;
-       double  pad_alt;
-       double  pad_lat_total;
-       double  pad_lon_total;
-       double  pad_alt_total;
-       int     npad;
-       int     prev_npad;
-
-       double  distance;
-       double  bearing;
-       int     gps_height;
-
-       int     speak_tick;
-       int     speak_altitude;
-};
-
-extern struct aostate aostate;
-
-/* GPS is 'stable' when we've seen at least this many samples */
-#define MIN_PAD_SAMPLES        10
-
-void
-aoview_monitor_disconnect(void);
-
-gboolean
-aoview_monitor_connect(char *tty);
-
-gboolean
-aoview_monitor_parse(const char *line);
-
-void
-aoview_monitor_reset(void);
-
-struct aoview_serial *
-aoview_serial_open(const char *tty);
-
-void
-aoview_serial_close(struct aoview_serial *serial);
-
-typedef void (*aoview_serial_callback)(gpointer user_data, struct aoview_serial *serial, gint revents);
-
-void
-aoview_serial_set_callback(struct aoview_serial *serial,
-                          aoview_serial_callback func);
-
-void
-aoview_serial_printf(struct aoview_serial *serial, char *format, ...);
-
-int
-aoview_serial_read(struct aoview_serial *serial, char *buf, int len);
-
-int
-aoview_serial_getc(struct aoview_serial *serial);
-
-void
-aoview_dev_dialog_init(GladeXML *xml);
-
-int
-aoview_usb_scan(struct usbdev ***devs_ret);
-
-void
-aoview_usbdev_free(struct usbdev *usbdev);
-
-void
-aoview_state_notify(struct aodata *data);
-
-void
-aoview_state_new(void);
-
-void
-aoview_state_init(GladeXML *xml);
-
-int16_t
-aoview_pres_to_altitude(int16_t pres);
-
-int16_t
-aoview_altitude_to_pres(int16_t alt);
-
-char *
-aoview_fullname (char *dir, char *file);
-
-char *
-aoview_basename(char *file);
-
-GtkTreeViewColumn *
-aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width);
-
-int
-aoview_mkdir(char *dir);
-
-void
-aoview_log_init(GladeXML *xml);
-
-void
-aoview_log_set_serial(int serial);
-
-int
-aoview_log_get_serial(void);
-
-void
-aoview_log_printf(char *format, ...);
-
-void
-aoview_log_new(void);
-
-void
-aoview_table_start(void);
-
-void
-aoview_table_add_row(int column, char *label, char *format, ...);
-
-void
-aoview_table_finish(void);
-
-void
-aoview_table_init(GladeXML *xml);
-
-void
-aoview_table_clear(void);
-
-struct aoview_file;
-
-extern char *aoview_file_dir;
-
-void
-aoview_file_finish(struct aoview_file *file);
-
-gboolean
-aoview_file_start(struct aoview_file *file);
-
-const char *
-aoview_file_name(struct aoview_file *file);
-
-void
-aoview_file_set_serial(struct aoview_file *file, int serial);
-
-int
-aoview_file_get_serial(struct aoview_file *file);
-
-void
-aoview_file_printf(struct aoview_file *file, char *format, ...);
-
-void
-aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap);
-
-struct aoview_file *
-aoview_file_new(char *ext);
-
-void
-aoview_file_destroy(struct aoview_file *file);
-
-void
-aoview_file_init(GladeXML *xml);
-
-/* aoview_eeprom.c */
-
-gboolean
-aoview_eeprom_save(const char *device);
-
-void
-aoview_eeprom_init(GladeXML *xml);
-
-/* aoview_voice.c */
-void aoview_voice_open(void);
-
-void aoview_voice_close(void);
-
-void aoview_voice_speak(char *format, ...);
-
-/* aoview_label.c */
-
-void aoview_label_init(GladeXML *xml);
-
-void
-aoview_label_show(struct aostate *state);
-
-/* aoview_flite.c */
-
-FILE *
-aoview_flite_start(void);
-
-void
-aoview_flite_stop(void);
-
-/* aoview_main.c */
-
-extern char *aoview_tty;
-
-#endif /* _AOVIEW_H_ */
diff --git a/ao-view/aoview_convert.c b/ao-view/aoview_convert.c
deleted file mode 100644 (file)
index 0241664..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-static int16_t altitude_table[2048] = {
-#include "altitude.h"
-};
-
-int16_t
-aoview_pres_to_altitude(int16_t pres)
-{
-       pres = pres >> 4;
-       if (pres < 0) pres = 0;
-       if (pres > 2047) pres = 2047;
-       return altitude_table[pres];
-}
-
-int16_t
-aoview_altitude_to_pres(int16_t alt)
-{
-       int16_t pres;
-
-       for (pres = 0; pres < 2047; pres++)
-               if (altitude_table[pres] <= alt)
-                       break;
-       return pres << 4;
-}
diff --git a/ao-view/aoview_dev.c b/ao-view/aoview_dev.c
deleted file mode 100644 (file)
index 9b8cc19..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-#include <ctype.h>
-#include <dirent.h>
-
-static char *
-load_string(char *dir, char *file)
-{
-       char    *full = aoview_fullname(dir, file);
-       char    line[4096];
-       char    *r;
-       FILE    *f;
-       int     rlen;
-
-       f = fopen(full, "r");
-       free(full);
-       if (!f)
-               return NULL;
-       r = fgets(line, sizeof (line), f);
-       fclose(f);
-       if (!r)
-               return NULL;
-       rlen = strlen(r);
-       if (r[rlen-1] == '\n')
-               r[rlen-1] = '\0';
-       return strdup(r);
-}
-
-static int
-load_hex(char *dir, char *file)
-{
-       char    *line;
-       char    *end;
-       long    i;
-
-       line = load_string(dir, file);
-       if (!line)
-               return -1;
-       i = strtol(line, &end, 16);
-       free(line);
-       if (end == line)
-               return -1;
-       return i;
-}
-
-static int
-dir_filter_tty_colon(const struct dirent *d)
-{
-       return strncmp(d->d_name, "tty:", 4) == 0;
-}
-
-static int
-dir_filter_tty(const struct dirent *d)
-{
-       return strncmp(d->d_name, "tty", 3) == 0;
-}
-
-static char *
-usb_tty(char *sys)
-{
-       char *base;
-       int num_configs;
-       int config;
-       struct dirent **namelist;
-       int interface;
-       int num_interfaces;
-       char endpoint_base[20];
-       char *endpoint_full;
-       char *tty_dir;
-       int ntty;
-       char *tty;
-
-       base = aoview_basename(sys);
-       num_configs = load_hex(sys, "bNumConfigurations");
-       num_interfaces = load_hex(sys, "bNumInterfaces");
-       for (config = 1; config <= num_configs; config++) {
-               for (interface = 0; interface < num_interfaces; interface++) {
-                       sprintf(endpoint_base, "%s:%d.%d",
-                               base, config, interface);
-                       endpoint_full = aoview_fullname(sys, endpoint_base);
-
-                       /* Check for tty:ttyACMx style names
-                        */
-                       ntty = scandir(endpoint_full, &namelist,
-                                      dir_filter_tty_colon,
-                                      alphasort);
-                       if (ntty > 0) {
-                               free(endpoint_full);
-                               tty = aoview_fullname("/dev", namelist[0]->d_name + 4);
-                               free(namelist);
-                               return tty;
-                       }
-
-                       /* Check for tty/ttyACMx style names
-                        */
-                       tty_dir = aoview_fullname(endpoint_full, "tty");
-                       free(endpoint_full);
-                       ntty = scandir(tty_dir, &namelist,
-                                      dir_filter_tty,
-                                      alphasort);
-                       free (tty_dir);
-                       if (ntty > 0) {
-                               tty = aoview_fullname("/dev", namelist[0]->d_name);
-                               free(namelist);
-                               return tty;
-                       }
-               }
-       }
-       return NULL;
-}
-
-static struct usbdev *
-usb_scan_device(char *sys)
-{
-       struct usbdev *usbdev;
-
-       usbdev = calloc(1, sizeof (struct usbdev));
-       if (!usbdev)
-               return NULL;
-       usbdev->sys = strdup(sys);
-       usbdev->manufacturer = load_string(sys, "manufacturer");
-       usbdev->product = load_string(sys, "product");
-       usbdev->serial = load_string(sys, "serial");
-       usbdev->idProduct = load_hex(sys, "idProduct");
-       usbdev->idVendor = load_hex(sys, "idVendor");
-       usbdev->tty = usb_tty(sys);
-       return usbdev;
-}
-
-void
-aoview_usbdev_free(struct usbdev *usbdev)
-{
-       free(usbdev->sys);
-       free(usbdev->manufacturer);
-       free(usbdev->product);
-       free(usbdev->serial);
-       free(usbdev->tty);
-       free(usbdev);
-}
-
-#define USB_DEVICES    "/sys/bus/usb/devices"
-
-static int
-dir_filter_dev(const struct dirent *d)
-{
-       const char      *n = d->d_name;
-       char    c;
-
-       while ((c = *n++)) {
-               if (isdigit(c))
-                       continue;
-               if (c == '-')
-                       continue;
-               if (c == '.' && n != d->d_name + 1)
-                       continue;
-               return 0;
-       }
-       return 1;
-}
-
-int
-aoview_usb_scan(struct usbdev ***devs_ret)
-{
-       int             n;
-       int             ndev = 0;
-       int             e;
-       struct dirent   **ents;
-       char            *dir;
-       struct usbdev   **devs = NULL;
-       struct usbdev   *dev;
-
-       n = scandir (USB_DEVICES, &ents,
-                    dir_filter_dev,
-                    alphasort);
-       if (!n)
-               return 0;
-       for (e = 0; e < n; e++) {
-               dir = aoview_fullname(USB_DEVICES, ents[e]->d_name);
-               dev = usb_scan_device(dir);
-               free(dir);
-               if (dev->idVendor == 0xfffe && dev->tty) {
-                       if (devs)
-                               devs = realloc(devs, ndev + 1 * sizeof (struct usbdev *));
-                       else
-                               devs = malloc (sizeof (struct usbdev *));
-                       devs[ndev++] = dev;
-               }
-       }
-       free(ents);
-       *devs_ret = devs;
-       return ndev;
-}
diff --git a/ao-view/aoview_dev_dialog.c b/ao-view/aoview_dev_dialog.c
deleted file mode 100644 (file)
index 3f92085..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-static void
-aoview_dev_dialog_map(GtkWidget *widget, gpointer data)
-{
-       GtkTreeView     *dev_list = data;
-       GtkListStore    *list_store;
-       GtkTreeIter     iter;
-       int             ndev, n;
-       struct usbdev   **devs;
-
-       list_store = gtk_list_store_new(3,
-                                       G_TYPE_STRING,
-                                       G_TYPE_STRING,
-                                       G_TYPE_STRING);
-
-       ndev = aoview_usb_scan(&devs);
-       for (n = 0; n < ndev; n++) {
-               gtk_list_store_append(list_store, &iter);
-               gtk_list_store_set(list_store, &iter,
-                                  0, devs[n]->product,
-                                  1, devs[n]->serial,
-                                  2, devs[n]->tty,
-                                  -1);
-       }
-       gtk_tree_view_set_model (dev_list, GTK_TREE_MODEL(list_store));
-       g_object_unref(G_OBJECT(list_store));
-       gtk_tree_view_columns_autosize(dev_list);
-}
-
-static GtkMessageDialog *dev_open_fail_dialog;
-
-static void
-aoview_dev_open_failed(char *name)
-{
-       char    *utf8_file;
-       utf8_file = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
-       if (!utf8_file)
-               utf8_file = name;
-       gtk_message_dialog_format_secondary_text(dev_open_fail_dialog,
-                                                "\"%s\"", utf8_file);
-       if (utf8_file != name)
-               g_free(utf8_file);
-       gtk_dialog_run(GTK_DIALOG(dev_open_fail_dialog));
-       gtk_widget_hide(GTK_WIDGET(dev_open_fail_dialog));
-}
-
-gboolean       dialog_save_log;
-
-static void
-aoview_dev_selected(GtkTreeModel *model,
-                   GtkTreePath *path,
-                   GtkTreeIter *iter,
-                   gpointer data)
-{
-       gchar *string;
-       gtk_tree_model_get(model, iter,
-                          2, &string,
-                          -1);
-       if (dialog_save_log) {
-               dialog_save_log = FALSE;
-               if (!aoview_eeprom_save(string))
-                       aoview_dev_open_failed(string);
-       } else {
-               if (!aoview_monitor_connect(string))
-                       aoview_dev_open_failed(string);
-       }
-}
-
-static GtkWidget       *dialog;
-
-static void
-aoview_dev_dialog_connect(GtkWidget *widget, gpointer data)
-{
-       GtkTreeView             *dev_list = data;
-       GtkListStore            *list_store;
-       GtkTreeSelection        *tree_selection;
-
-       list_store = GTK_LIST_STORE(gtk_tree_view_get_model(dev_list));
-       tree_selection = gtk_tree_view_get_selection(dev_list);
-       gtk_tree_selection_selected_foreach(tree_selection,
-                                           aoview_dev_selected,
-                                           data);
-       gtk_widget_hide(dialog);
-}
-
-static void
-aoview_dev_disconnect(GtkWidget *widget)
-{
-       aoview_monitor_disconnect();
-}
-
-static void
-aoview_dev_savelog(GtkWidget *widget, gpointer data)
-{
-       dialog_save_log = TRUE;
-       gtk_widget_show(dialog);
-}
-
-#define _(a) a
-
-void
-aoview_dev_dialog_init(GladeXML *xml)
-{
-       GtkTreeView     *dev_list;
-       GtkWidget       *connect_button;
-       GtkTreeSelection        *dev_selection;
-       GtkWidget       *ao_disconnect;
-       GtkWidget       *ao_savelog;
-
-       dialog = glade_xml_get_widget(xml, "device_connect_dialog");
-       assert(dialog);
-
-       dev_list = GTK_TREE_VIEW(glade_xml_get_widget(xml, "dev_list"));
-       assert(dev_list);
-
-       aoview_add_plain_text_column(dev_list, _("Product"), 0, 16);
-       aoview_add_plain_text_column(dev_list, _("Serial"),  1, 8);
-       aoview_add_plain_text_column(dev_list, _("Device"), 2, 13);
-
-       dev_selection = gtk_tree_view_get_selection(dev_list);
-       gtk_tree_selection_set_mode(dev_selection, GTK_SELECTION_SINGLE);
-
-       g_signal_connect(G_OBJECT(dialog), "map",
-                        G_CALLBACK(aoview_dev_dialog_map),
-                        dev_list);
-
-       connect_button = glade_xml_get_widget(xml, "connect_button");
-       assert(connect_button);
-
-       g_signal_connect(G_OBJECT(connect_button), "clicked",
-                        G_CALLBACK(aoview_dev_dialog_connect),
-                        dev_list);
-
-
-       ao_disconnect = glade_xml_get_widget(xml, "ao_disconnect");
-       assert(ao_disconnect);
-
-       g_signal_connect(G_OBJECT(ao_disconnect), "activate",
-                        G_CALLBACK(aoview_dev_disconnect),
-                        ao_disconnect);
-
-       ao_savelog = glade_xml_get_widget(xml, "ao_savelog");
-       assert(ao_savelog);
-
-       g_signal_connect(G_OBJECT(ao_savelog), "activate",
-                        G_CALLBACK(aoview_dev_savelog),
-                        dialog);
-       dev_open_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "dev_open_fail_dialog"));
-       assert(dev_open_fail_dialog);
-}
diff --git a/ao-view/aoview_eeprom.c b/ao-view/aoview_eeprom.c
deleted file mode 100644 (file)
index 34e2dee..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-#define EEPROM_LEN     1024
-
-static struct aoview_file      *eeprom_file;
-static char                    eeprom_line[EEPROM_LEN + 1];
-static int                     eeprom_pos;
-static GtkMessageDialog                *eeprom_save_done;
-static GtkWidget               *eeprom_save_close;
-static gboolean                        eeprom_save_shown;
-
-static void
-aoview_eeprom_disconnect(struct aoview_serial *serial)
-{
-       aoview_file_finish(eeprom_file);
-}
-
-static void
-aoview_eeprom_done(struct aoview_serial *serial)
-{
-       gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
-                            "EEPROM data saved");
-       gtk_message_dialog_set_markup(eeprom_save_done,
-                                     "<b>EEPROM data saved as</b>");
-       if (!eeprom_save_shown)
-               gtk_widget_show(GTK_WIDGET(eeprom_save_done));
-       eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
-       if (eeprom_save_close)
-               gtk_widget_set_sensitive(eeprom_save_close, TRUE);
-       aoview_eeprom_disconnect(serial);
-}
-
-static gboolean
-aoview_eeprom_parse(struct aoview_serial *serial,
-                   char *line)
-{
-       char            cmd;
-       int             tick;
-       int             a;
-       int             b;
-       int             serial_number;
-       const char      *name;
-       char            *utf8_name;
-
-       if (!strcmp(line, "end")) {
-               aoview_eeprom_done(serial);
-               return FALSE;
-       }
-       if (sscanf(line, "serial-number %u", &serial_number) == 1) {
-               aoview_file_set_serial(eeprom_file, serial_number);
-       } else if (sscanf(line, "%c %x %x %x", &cmd, &tick, &a, &b) == 4) {
-               aoview_file_printf(eeprom_file, "%s\n", line);
-               if (cmd == 'S' && a == 8) {
-                       aoview_eeprom_done(serial);
-                       return FALSE;
-               }
-
-               if (!eeprom_save_shown)
-               {
-                       name = aoview_file_name(eeprom_file);
-                       if (name) {
-                               utf8_name = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
-                               if (!utf8_name)
-                                       utf8_name = (char *) name;
-                               gtk_widget_set_sensitive(eeprom_save_close, FALSE);
-                               gtk_window_set_title(GTK_WINDOW(eeprom_save_done),
-                                                    "Saving EEPROM data");
-                               gtk_message_dialog_set_markup(eeprom_save_done,
-                                                             "<b>Saving EEPROM data as</b>");
-                               gtk_message_dialog_format_secondary_text(eeprom_save_done, "%s",
-                                                                        utf8_name);
-                               if (utf8_name != name)
-                                       g_free(utf8_name);
-                               gtk_container_check_resize(GTK_CONTAINER(eeprom_save_done));
-                               gtk_widget_show(GTK_WIDGET(eeprom_save_done));
-                               eeprom_save_shown = TRUE;
-                               eeprom_save_close = gtk_window_get_default_widget(GTK_WINDOW(eeprom_save_done));
-                               if (eeprom_save_close)
-                                       gtk_widget_set_sensitive(eeprom_save_close, FALSE);
-                       }
-               }
-       }
-       return TRUE;
-}
-
-static void
-aoview_eeprom_callback(gpointer user_data,
-                      struct aoview_serial *serial,
-                      gint revents)
-{
-       int     c;
-
-       if (revents & (G_IO_HUP|G_IO_ERR)) {
-               aoview_eeprom_disconnect(serial);
-               return;
-       }
-       if (revents & G_IO_IN) {
-               for (;;) {
-                       c = aoview_serial_getc(serial);
-                       if (c == -1)
-                               break;
-                       if (c == '\r')
-                               continue;
-                       if (c == '\n') {
-                               eeprom_line[eeprom_pos] = '\0';
-                               if (eeprom_pos)
-                               if (!aoview_eeprom_parse(serial, eeprom_line))
-                                       break;
-                               eeprom_pos = 0;
-                       } else if (eeprom_pos < EEPROM_LEN)
-                               eeprom_line[eeprom_pos++] = c;
-               }
-       }
-}
-
-gboolean
-aoview_eeprom_save(const char *device)
-{
-       struct aoview_serial    *serial;
-
-       gtk_widget_hide(GTK_WIDGET(eeprom_save_done));
-       eeprom_save_shown = FALSE;
-       serial = aoview_serial_open(device);
-       if (!serial)
-               return FALSE;
-       aoview_serial_set_callback(serial, aoview_eeprom_callback);
-       aoview_serial_printf(serial, "v\nl\n");
-       return TRUE;
-}
-
-void
-aoview_eeprom_init(GladeXML *xml)
-{
-       eeprom_file = aoview_file_new("eeprom");
-       assert(eeprom_file);
-
-       eeprom_save_done = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "ao_save_done"));
-       assert(eeprom_save_done);
-
-}
diff --git a/ao-view/aoview_file.c b/ao-view/aoview_file.c
deleted file mode 100644 (file)
index 5288c2f..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-char *aoview_file_dir;
-
-#define ALTOS_DIR_PATH "/apps/aoview/log_dir"
-#define DEFAULT_DIR    "AltOS"
-
-struct aoview_file {
-       char    *ext;
-       FILE    *file;
-       char    *name;
-       int     failed;
-       int     serial;
-       int     sequence;
-};
-
-static void
-aoview_file_save_conf(void)
-{
-       GConfClient     *gconf_client;
-
-       gconf_client = gconf_client_get_default();
-       if (gconf_client)
-       {
-               gconf_client_set_string(gconf_client,
-                                       ALTOS_DIR_PATH,
-                                       aoview_file_dir,
-                                       NULL);
-               g_object_unref(G_OBJECT(gconf_client));
-       }
-}
-
-static void
-aoview_file_configure(GtkWidget *widget, gpointer data)
-{
-       GtkFileChooser *chooser = data;
-       aoview_file_dir = gtk_file_chooser_get_filename(chooser);
-       aoview_file_save_conf();
-       gtk_widget_hide(GTK_WIDGET(chooser));
-}
-
-void
-aoview_file_finish(struct aoview_file *file)
-{
-       if (file->file) {
-               fclose(file->file);
-               file->file = NULL;
-               free(file->name);
-               file->name = NULL;
-       }
-       file->failed = 0;
-}
-
-const char *
-aoview_file_name(struct aoview_file *file)
-{
-       return file->name;
-}
-
-static GtkMessageDialog *file_fail_dialog;
-
-static void
-aoview_file_open_failed(char *name)
-{
-       char    *utf8_file;
-       utf8_file = g_filename_to_utf8(name, -1, NULL, NULL, NULL);
-       if (!utf8_file)
-               utf8_file = name;
-       gtk_message_dialog_format_secondary_text(file_fail_dialog,
-                                                "\"%s\"", utf8_file);
-       if (utf8_file != name)
-               g_free(utf8_file);
-       gtk_widget_show(GTK_WIDGET(file_fail_dialog));
-}
-
-gboolean
-aoview_file_start(struct aoview_file *file)
-{
-       char            base[50];
-       struct tm       tm;
-       time_t          now;
-       char            *full;
-       int             r;
-
-       if (file->file)
-               return TRUE;
-
-       if (file->failed)
-               return FALSE;
-
-       now = time(NULL);
-       (void) localtime_r(&now, &tm);
-       aoview_mkdir(aoview_file_dir);
-       for (;;) {
-               snprintf(base, sizeof (base), "%04d-%02d-%02d-serial-%03d-flight-%03d.%s",
-                       tm.tm_year + 1900,
-                       tm.tm_mon + 1,
-                       tm.tm_mday,
-                       file->serial,
-                       file->sequence,
-                       file->ext);
-               full = aoview_fullname(aoview_file_dir, base);
-               r = access(full, F_OK);
-               if (r < 0) {
-                       file->file = fopen(full, "w");
-                       if (!file->file) {
-                               aoview_file_open_failed(full);
-                               free(full);
-                               file->failed = 1;
-                               return FALSE;
-                       } else {
-                               setlinebuf(file->file);
-                               file->name = full;
-                               return TRUE;
-                       }
-               }
-               free(full);
-               file->sequence++;
-       }
-}
-
-void
-aoview_file_vprintf(struct aoview_file *file, char *format, va_list ap)
-{
-       if (!aoview_file_start(file))
-               return;
-       vfprintf(file->file, format, ap);
-}
-
-void
-aoview_file_printf(struct aoview_file *file, char *format, ...)
-{
-       va_list ap;
-
-       va_start(ap, format);
-       aoview_file_vprintf(file, format, ap);
-       va_end(ap);
-}
-
-struct aoview_file *
-aoview_file_new(char *ext)
-{
-       struct aoview_file      *file;
-
-       file = calloc (1, sizeof (struct aoview_file));
-       if (!file)
-               return NULL;
-       file->ext = strdup(ext);
-       if (!file->ext) {
-               free(file);
-               return NULL;
-       }
-       return file;
-}
-
-void
-aoview_file_destroy(struct aoview_file *file)
-{
-       if (file->file)
-               fclose(file->file);
-       if (file->name)
-               free(file->name);
-       free(file->ext);
-       free(file);
-}
-
-void
-aoview_file_set_serial(struct aoview_file *file, int serial)
-{
-       if (serial != file->serial)
-               aoview_file_finish(file);
-       file->serial = serial;
-}
-
-int
-aoview_file_get_serial(struct aoview_file *file)
-{
-       return file->serial;
-}
-
-void
-aoview_file_init(GladeXML *xml)
-{
-       GConfClient     *gconf_client;
-       char            *file_dir = NULL;
-       GtkFileChooser  *file_chooser_dialog;
-       GtkWidget       *file_configure_ok;
-
-       g_type_init();
-       gconf_client = gconf_client_get_default();
-       if (gconf_client)
-       {
-               file_dir = gconf_client_get_string(gconf_client,
-                                                  ALTOS_DIR_PATH,
-                                                  NULL);
-               g_object_unref(G_OBJECT(gconf_client));
-       }
-       if (!file_dir) {
-               aoview_file_dir = aoview_fullname(getenv("HOME"), DEFAULT_DIR);
-               aoview_file_save_conf();
-       } else {
-               aoview_file_dir = strdup(file_dir);
-       }
-
-       file_chooser_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "file_chooser_dialog"));
-       assert(file_chooser_dialog);
-       gtk_file_chooser_set_filename(file_chooser_dialog, aoview_file_dir);
-
-       file_configure_ok = glade_xml_get_widget(xml, "file_configure_ok");
-       assert(file_configure_ok);
-
-       g_signal_connect(G_OBJECT(file_configure_ok), "clicked",
-                        G_CALLBACK(aoview_file_configure),
-                        file_chooser_dialog);
-
-
-       file_fail_dialog = GTK_MESSAGE_DIALOG(glade_xml_get_widget(xml, "file_fail_dialog"));
-       assert(file_fail_dialog);
-}
diff --git a/ao-view/aoview_flite.c b/ao-view/aoview_flite.c
deleted file mode 100644 (file)
index e1b7589..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include <stdio.h>
-#include <flite/flite.h>
-#include "aoview.h"
-#include <alsa/asoundlib.h>
-
-cst_voice *register_cmu_us_kal();
-static cst_voice *voice;
-
-static FILE *pipe_write;
-static GThread *aoview_flite_thread;
-
-static snd_pcm_t       *alsa_handle;
-
-gpointer
-aoview_flite_task(gpointer data)
-{
-       FILE            *input = data;
-       char            line[1024];
-       cst_wave        *wave;
-       int             rate;
-       int             channels;
-       int             err;
-       char            *samples;
-       int             num_samples;
-
-       err = snd_pcm_open(&alsa_handle, "default",
-                          SND_PCM_STREAM_PLAYBACK, 0);
-       if (err >= 0)
-       {
-               if (err < 0) {
-                       snd_pcm_close(alsa_handle);
-                       alsa_handle = 0;
-               }
-       }
-       rate = 0;
-       channels = 0;
-       while (fgets(line, sizeof (line) - 1, input) != NULL) {
-               if (!alsa_handle)
-                       continue;
-               wave = flite_text_to_wave(line, voice);
-               if (wave->sample_rate != rate ||
-                   wave->num_channels != channels)
-               {
-                       rate = wave->sample_rate;
-                       channels = wave->num_channels;
-                       err = snd_pcm_set_params(alsa_handle,
-                                                SND_PCM_FORMAT_S16,
-                                                SND_PCM_ACCESS_RW_INTERLEAVED,
-                                                channels,
-                                                rate,
-                                                1,
-                                                100000);
-                       if (err < 0)
-                               fprintf(stderr, "alsa set_params error %s\n",
-                                       strerror(-err));
-               }
-               err = snd_pcm_prepare(alsa_handle);
-               if (err < 0)
-                       fprintf(stderr, "alsa pcm_prepare error %s\n",
-                               strerror(-err));
-               samples = (char *) wave->samples;
-               num_samples = wave->num_samples;
-               while (num_samples > 0) {
-                       err = snd_pcm_writei(alsa_handle,
-                                            samples, num_samples);
-                       if (err <= 0) {
-                               fprintf(stderr, "alsa write error %s\n",
-                                       strerror(-err));
-                               break;
-                       }
-                       num_samples -= err;
-                       samples += err * 2 * channels;
-               }
-               snd_pcm_drain(alsa_handle);
-               delete_wave(wave);
-       }
-       snd_pcm_close(alsa_handle);
-       alsa_handle = 0;
-       return NULL;
-}
-
-void
-aoview_flite_stop(void)
-{
-       int status;
-       if (pipe_write) {
-               fclose(pipe_write);
-               pipe_write = NULL;
-       }
-       if (aoview_flite_thread) {
-               g_thread_join(aoview_flite_thread);
-               aoview_flite_thread = NULL;
-       }
-}
-
-FILE *
-aoview_flite_start(void)
-{
-       static once;
-       int     p[2];
-       GError  *error;
-       FILE    *pipe_read;
-
-       if (!once) {
-               flite_init();
-               voice = register_cmu_us_kal();
-               if (!voice) {
-                       perror("register voice");
-                       exit(1);
-               }
-       }
-       aoview_flite_stop();
-       pipe(p);
-       pipe_read = fdopen(p[0], "r");
-       pipe_write = fdopen(p[1], "w");
-       g_thread_create(aoview_flite_task, pipe_read, TRUE, &error);
-       return pipe_write;
-}
diff --git a/ao-view/aoview_label.c b/ao-view/aoview_label.c
deleted file mode 100644 (file)
index 2431362..0000000
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-static struct {
-       char            *name;
-       char            *initial_value;
-       GtkLabel        *widget;
-} label_widgets[] = {
-       { "height_label", "Height (m)", NULL },
-       { "state_label", "State", NULL },
-       { "rssi_label", "RSSI (dBm)", NULL },
-       { "speed_label", "Speed (m/s)", NULL },
-       { "height_value", "0", NULL },
-       { "state_value", "pad", NULL },
-       { "rssi_value", "-50", NULL },
-       { "speed_value", "0", NULL },
-};
-
-static void
-aoview_label_assign(GtkLabel *widget, char *value)
-{
-       char    *markup;
-
-       markup = g_markup_printf_escaped("<span font_weight=\"bold\" size=\"xx-large\">%s</span>", value);
-       gtk_label_set_markup(widget, markup);
-       g_free(markup);
-}
-
-void
-aoview_label_show(struct aostate *state)
-{
-       char    line[1024];
-       sprintf(line, "%d", state->height);
-       aoview_label_assign(label_widgets[4].widget, line);
-
-       aoview_label_assign(label_widgets[5].widget, state->data.state);
-
-       sprintf(line, "%d", state->data.rssi);
-       aoview_label_assign(label_widgets[6].widget, line);
-
-       if (state->ascent)
-               sprintf(line, "%6.0f", fabs(state->speed));
-       else
-               sprintf(line, "%6.0f", fabs(state->baro_speed));
-       aoview_label_assign(label_widgets[7].widget, line);
-}
-
-void
-aoview_label_init(GladeXML *xml)
-{
-       int i;
-       for (i = 0; i < sizeof(label_widgets)/sizeof(label_widgets[0]); i++) {
-               label_widgets[i].widget = GTK_LABEL(glade_xml_get_widget(xml, label_widgets[i].name));
-               aoview_label_assign(label_widgets[i].widget, label_widgets[i].initial_value);
-               assert(label_widgets[i].widget);
-       }
-}
diff --git a/ao-view/aoview_log.c b/ao-view/aoview_log.c
deleted file mode 100644 (file)
index 1b89c28..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-static struct aoview_file      *aoview_log;
-
-void
-aoview_log_new(void)
-{
-       aoview_file_finish(aoview_log);
-       aoview_state_new();
-}
-
-void
-aoview_log_set_serial(int serial)
-{
-       aoview_file_set_serial(aoview_log, serial);
-}
-
-int
-aoview_log_get_serial(void)
-{
-       return aoview_file_get_serial(aoview_log);
-}
-
-void
-aoview_log_printf(char *format, ...)
-{
-       va_list ap;
-
-       va_start(ap, format);
-       aoview_file_vprintf(aoview_log, format, ap);
-       va_end(ap);
-}
-
-static void
-aoview_log_new_item(GtkWidget *widget, gpointer data)
-{
-       aoview_file_finish(aoview_log);
-}
-
-void
-aoview_log_init(GladeXML *xml)
-{
-       GtkWidget       *log_new;
-
-       aoview_log = aoview_file_new("telem");
-       assert(aoview_log);
-
-       log_new = glade_xml_get_widget(xml, "log_new");
-       assert(log_new);
-       g_signal_connect(G_OBJECT(log_new), "activate",
-                        G_CALLBACK(aoview_log_new_item),
-                        NULL);
-}
diff --git a/ao-view/aoview_main.c b/ao-view/aoview_main.c
deleted file mode 100644 (file)
index 64c1c02..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-static const char aoview_glade[] = {
-#include "aoview_glade.h"
-};
-
-static void usage(void) {
-       printf("aoview [--device|-d device_file]");
-       exit(1);
-}
-
-static void destroy_event(GtkWidget *widget, gpointer data)
-{
-       gtk_main_quit();
-}
-
-extern int _Xdebug;
-char *aoview_tty = NULL;
-
-int main(int argc, char **argv)
-{
-       GladeXML *xml = NULL;
-       GtkWidget *mainwindow;
-       GtkAboutDialog *about_dialog;
-
-       static struct option long_options[] = {
-               { "tty", 1, 0, 'T'},
-               { "sync", 0, 0, 's'},
-               { 0, 0, 0, 0 }
-       };
-       for (;;) {
-               int c, temp;
-
-               c = getopt_long_only(argc, argv, "sT:", long_options, &temp);
-               if (c == -1)
-                       break;
-
-               switch (c) {
-               case 'T':
-                       aoview_tty = optarg;
-                       break;
-               case 's':
-                       _Xdebug = 1;
-                       break;
-               default:
-                       usage();
-               }
-       }
-
-       g_thread_init(NULL);
-       gtk_init(&argc, &argv);
-       glade_init();
-
-       xml = glade_xml_new_from_buffer(aoview_glade, sizeof (aoview_glade), NULL, NULL);
-
-       /* connect the signals in the interface */
-       glade_xml_signal_autoconnect(xml);
-
-       /* Hook up the close button. */
-       mainwindow = glade_xml_get_widget(xml, "aoview");
-       assert(mainwindow);
-
-       g_signal_connect (G_OBJECT(mainwindow), "destroy",
-           G_CALLBACK(destroy_event), NULL);
-
-       about_dialog = GTK_ABOUT_DIALOG(glade_xml_get_widget(xml, "about_dialog"));
-       assert(about_dialog);
-       gtk_about_dialog_set_version(about_dialog, AOVIEW_VERSION);
-
-       aoview_voice_init(xml);
-
-       aoview_dev_dialog_init(xml);
-
-       aoview_state_init(xml);
-
-       aoview_file_init(xml);
-
-       aoview_log_init(xml);
-
-       aoview_table_init(xml);
-
-       aoview_eeprom_init(xml);
-
-       aoview_replay_init(xml);
-
-       aoview_label_init(xml);
-
-       if (aoview_tty) {
-               if (!aoview_monitor_connect(aoview_tty)) {
-                       perror(aoview_tty);
-                       exit(1);
-               }
-       }
-       aoview_voice_speak("rocket flight monitor ready\n");
-
-       gtk_main();
-
-       return 0;
-}
diff --git a/ao-view/aoview_monitor.c b/ao-view/aoview_monitor.c
deleted file mode 100644 (file)
index 9265a19..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-static struct aoview_serial *monitor_serial;
-
-#define MONITOR_LEN    1024
-
-static char    monitor_line[MONITOR_LEN + 1];
-static int     monitor_pos;
-
-void
-aoview_monitor_disconnect(void)
-{
-       if (monitor_serial) {
-               aoview_serial_close(monitor_serial);
-               monitor_serial = NULL;
-       }
-       aoview_log_new();
-}
-
-static void
-aoview_parse_string(char *target, int len, char *source)
-{
-       strncpy(target, source, len-1);
-       target[len-1] = '\0';
-}
-
-static void
-aoview_parse_int(int *target, char *source)
-{
-       *target = strtol(source, NULL, 0);
-}
-
-static void
-aoview_parse_pos(double *target, char *source)
-{
-       int     deg;
-       double  min;
-       char    dir;
-       double  r;
-
-       if (sscanf(source, "%d°%lf'%c", &deg, &min, &dir) != 3) {
-               *target = 0;
-               return;
-       }
-       r = deg + min / 60.0;
-       if (dir == 'S' || dir == 'W')
-               r = -r;
-       *target = r;
-}
-
-gboolean
-aoview_monitor_parse(const char *input_line)
-{
-       char *saveptr;
-       char *words[64];
-       int nword;
-       char line_buf[8192], *line;
-       struct aodata   data;
-
-       /* avoid smashing our input parameter */
-       strncpy (line_buf, input_line, sizeof (line_buf)-1);
-       line_buf[sizeof(line_buf) - 1] = '\0';
-       line = line_buf;
-       for (nword = 0; nword < 64; nword++) {
-               words[nword] = strtok_r(line, " \t\n", &saveptr);
-               line = NULL;
-               if (words[nword] == NULL)
-                       break;
-       }
-       if (nword < 36)
-               return FALSE;
-       if (strcmp(words[0], "CALL") != 0)
-               return FALSE;
-       aoview_parse_string(data.callsign, sizeof (data.callsign), words[1]);
-       aoview_parse_int(&data.serial, words[3]);
-
-       aoview_parse_int(&data.rssi, words[5]);
-       aoview_parse_string(data.state, sizeof (data.state), words[9]);
-       aoview_parse_int(&data.tick, words[10]);
-       aoview_parse_int(&data.accel, words[12]);
-       aoview_parse_int(&data.pres, words[14]);
-       aoview_parse_int(&data.temp, words[16]);
-       aoview_parse_int(&data.batt, words[18]);
-       aoview_parse_int(&data.drogue, words[20]);
-       aoview_parse_int(&data.main, words[22]);
-       aoview_parse_int(&data.flight_accel, words[24]);
-       aoview_parse_int(&data.ground_accel, words[26]);
-       aoview_parse_int(&data.flight_vel, words[28]);
-       aoview_parse_int(&data.flight_pres, words[30]);
-       aoview_parse_int(&data.ground_pres, words[32]);
-       aoview_parse_int(&data.gps.nsat, words[34]);
-       if (strcmp (words[36], "unlocked") == 0) {
-               data.gps.gps_connected = 1;
-               data.gps.gps_locked = 0;
-               data.gps.gps_time.hour = data.gps.gps_time.minute = data.gps.gps_time.second = 0;
-               data.gps.lat = data.gps.lon = 0;
-               data.gps.alt = 0;
-       } else if (nword >= 40) {
-               data.gps.gps_locked = 1;
-               data.gps.gps_connected = 1;
-               sscanf(words[36], "%d:%d:%d", &data.gps.gps_time.hour, &data.gps.gps_time.minute, &data.gps.gps_time.second);
-               aoview_parse_pos(&data.gps.lat, words[37]);
-               aoview_parse_pos(&data.gps.lon, words[38]);
-               sscanf(words[39], "%dm", &data.gps.alt);
-       } else {
-               data.gps.gps_connected = 0;
-               data.gps.gps_locked = 0;
-               data.gps.gps_time.hour = data.gps.gps_time.minute = data.gps.gps_time.second = 0;
-               data.gps.lat = data.gps.lon = 0;
-               data.gps.alt = 0;
-       }
-       if (nword >= 46) {
-               data.gps.gps_extended = 1;
-               sscanf(words[40], "%lfm/s", &data.gps.ground_speed);
-               sscanf(words[41], "%d", &data.gps.course);
-               sscanf(words[42], "%lfm/s", &data.gps.climb_rate);
-               sscanf(words[43], "%lf", &data.gps.hdop);
-               sscanf(words[44], "%d", &data.gps.h_error);
-               sscanf(words[45], "%d", &data.gps.v_error);
-       } else {
-               data.gps.gps_extended = 0;
-               data.gps.ground_speed = 0;
-               data.gps.course = 0;
-               data.gps.climb_rate = 0;
-               data.gps.hdop = 0;
-               data.gps.h_error = 0;
-               data.gps.v_error = 0;
-       }
-       aoview_state_notify(&data);
-       return TRUE;
-}
-
-static void
-aoview_monitor_callback(gpointer user_data,
-                       struct aoview_serial *serial,
-                       gint revents)
-{
-       int     c;
-
-       if (revents & (G_IO_HUP|G_IO_ERR)) {
-               aoview_monitor_disconnect();
-               return;
-       }
-       if (revents & G_IO_IN) {
-               for (;;) {
-                       c = aoview_serial_getc(serial);
-                       if (c == -1)
-                               break;
-                       if (c == '\r')
-                               continue;
-                       if (c == '\n') {
-                               monitor_line[monitor_pos] = '\0';
-                               if (monitor_pos) {
-                                       if (aoview_monitor_parse(monitor_line)) {
-                                               aoview_log_set_serial(aostate.data.serial);
-                                               if (aoview_log_get_serial())
-                                                       aoview_log_printf ("%s\n", monitor_line);
-                                       }
-                               }
-                               monitor_pos = 0;
-                       } else if (monitor_pos < MONITOR_LEN)
-                               monitor_line[monitor_pos++] = c;
-               }
-       }
-}
-
-gboolean
-aoview_monitor_connect(char *tty)
-{
-       aoview_monitor_disconnect();
-       monitor_serial = aoview_serial_open(tty);
-       if (!monitor_serial)
-               return FALSE;
-       aoview_table_clear();
-       aoview_state_reset();
-       aoview_serial_set_callback(monitor_serial,
-                                  aoview_monitor_callback);
-       return TRUE;
-}
diff --git a/ao-view/aoview_replay.c b/ao-view/aoview_replay.c
deleted file mode 100644 (file)
index da7b5d6..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-static GtkFileChooser  *replay_dialog;
-static GtkWidget       *replay_ok;
-static FILE            *replay_file;
-static int             replay_tick;
-
-static int
-find_tick(char *line, gboolean *is_pad)
-{
-       char    *state = strstr(line, "STATE");
-       if (!state)
-               return -1;
-       state = strchr(state, ' ');
-       if (!state)
-               return -1;
-       while (*state == ' ')
-               state++;
-       *is_pad = strncmp(state, "pad", 3) == 0;
-       while (*state && !isdigit(*state))
-               state++;
-       return atoi(state);
-}
-
-static void
-aoview_replay_close(void)
-{
-       if (replay_file) {
-               fclose(replay_file);
-               replay_file = NULL;
-       }
-}
-
-static char    replay_line[1024];
-
-static gboolean
-aoview_replay_read(gpointer data);
-
-static gboolean
-aoview_replay_execute(gpointer data)
-{
-       aoview_monitor_parse(replay_line);
-       g_idle_add(aoview_replay_read, NULL);
-       return FALSE;
-}
-
-static gboolean
-aoview_replay_read(gpointer data)
-{
-       int             tick;
-       gboolean        is_pad;
-
-       if (!replay_file)
-               return FALSE;
-       if (fgets(replay_line, sizeof (replay_line), replay_file)) {
-               tick = find_tick(replay_line, &is_pad);
-               if (tick >= 0 && replay_tick >= 0 && !is_pad) {
-                       while (tick < replay_tick)
-                               tick += 65536;
-                       g_timeout_add((tick - replay_tick) * 10,
-                                     aoview_replay_execute,
-                                     NULL);
-               } else {
-                       aoview_replay_execute(NULL);
-               }
-               replay_tick = tick;
-       } else {
-               aoview_replay_close();
-       }
-       return FALSE;
-}
-
-static void
-aoview_replay_open(GtkWidget *widget, gpointer data)
-{
-       char            *replay_file_name;
-       GtkWidget       *dialog;
-
-       aoview_replay_close();
-       replay_file_name = gtk_file_chooser_get_filename(replay_dialog);
-       replay_file = fopen(replay_file_name, "r");
-       if (!replay_file) {
-               dialog = gtk_message_dialog_new(GTK_WINDOW(replay_dialog),
-                                               GTK_DIALOG_DESTROY_WITH_PARENT,
-                                               GTK_MESSAGE_ERROR,
-                                               GTK_BUTTONS_CLOSE,
-                                               "Error loading file '%s': %s",
-                                               replay_file_name, g_strerror(errno));
-               gtk_dialog_run(GTK_DIALOG(dialog));
-               gtk_widget_destroy(dialog);
-       } else {
-               replay_tick = -1;
-               aoview_state_reset();
-               aoview_replay_read(NULL);
-       }
-       gtk_widget_hide(GTK_WIDGET(replay_dialog));
-}
-
-void
-aoview_replay_init(GladeXML *xml)
-{
-       GtkFileFilter   *telem_filter;
-       GtkFileFilter   *all_filter;
-       GtkFileFilter   *log_filter;
-
-       telem_filter = gtk_file_filter_new();
-       gtk_file_filter_add_pattern(telem_filter, "*.telem");
-       gtk_file_filter_set_name(telem_filter, "Telemetry Files");
-
-       log_filter = gtk_file_filter_new();
-       gtk_file_filter_add_pattern(log_filter, "*.log");
-       gtk_file_filter_set_name(log_filter, "Log Files");
-
-       all_filter = gtk_file_filter_new();
-       gtk_file_filter_add_pattern(all_filter, "*");
-       gtk_file_filter_set_name(all_filter, "All Files");
-
-       replay_dialog = GTK_FILE_CHOOSER(glade_xml_get_widget(xml, "ao_replay_dialog"));
-       assert(replay_dialog);
-       gtk_file_chooser_set_current_folder(replay_dialog, aoview_file_dir);
-       gtk_file_chooser_add_filter(replay_dialog, telem_filter);
-       gtk_file_chooser_add_filter(replay_dialog, log_filter);
-       gtk_file_chooser_add_filter(replay_dialog, all_filter);
-
-       replay_ok = glade_xml_get_widget(xml, "ao_replay_ok");
-       assert(replay_ok);
-       g_signal_connect(G_OBJECT(replay_ok), "clicked",
-                        G_CALLBACK(aoview_replay_open),
-                        replay_dialog);
-}
diff --git a/ao-view/aoview_serial.c b/ao-view/aoview_serial.c
deleted file mode 100644 (file)
index 29038b7..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-#include <termios.h>
-
-#define AOVIEW_SERIAL_IN_BUF   64
-#define AOVIEW_SERIAL_OUT_BUF  64
-
-struct aoview_buf {
-       char            *buf;
-       int             off;
-       int             count;
-       int             size;
-};
-
-static int
-aoview_buf_write(struct aoview_buf *buf, char *data, int len)
-{
-       if (buf->count + len > buf->size) {
-               int     new_size = buf->size * 2;
-               if (new_size == 0)
-                       new_size = 1024;
-               if (buf->buf)
-                       buf->buf = realloc (buf->buf, new_size);
-               else
-                       buf->buf = malloc (new_size);
-               buf->size = new_size;
-       }
-       memcpy(buf->buf + buf->count, data, len);
-       buf->count += len;
-       return len;
-}
-
-static int
-aoview_buf_read(struct aoview_buf *buf, char *data, int len)
-{
-       if (len > buf->count - buf->off)
-               len = buf->count - buf->off;
-       memcpy (data, buf->buf + buf->off, len);
-       buf->off += len;
-       if (buf->off == buf->count)
-               buf->off = buf->count = 0;
-       return len;
-}
-
-static int
-aoview_buf_getc(struct aoview_buf *buf)
-{
-       char    b;
-       int     r;
-
-       r = aoview_buf_read(buf, &b, 1);
-       if (r == 1)
-               return (int) b;
-       return -1;
-}
-
-static void
-aoview_buf_flush(struct aoview_buf *buf, int fd)
-{
-       int     ret;
-
-       if (buf->count > buf->off) {
-               ret = write(fd, buf->buf + buf->off, buf->count - buf->off);
-               if (ret > 0) {
-                       buf->off += ret;
-                       if (buf->off == buf->count)
-                               buf->off = buf->count = 0;
-               }
-       }
-}
-
-static void
-aoview_buf_fill(struct aoview_buf *buf, int fd)
-{
-       int ret;
-
-       while (buf->count >= buf->size) {
-               int new_size = buf->size * 2;
-               buf->buf = realloc (buf->buf, new_size);
-               buf->size = new_size;
-       }
-
-       ret = read(fd, buf->buf + buf->count, buf->size - buf->count);
-       if (ret > 0)
-               buf->count += ret;
-}
-
-static void
-aoview_buf_init(struct aoview_buf *buf)
-{
-       buf->buf = malloc (buf->size = 1024);
-       buf->count = 0;
-}
-
-static void
-aoview_buf_fini(struct aoview_buf *buf)
-{
-       free(buf->buf);
-}
-
-struct aoview_serial {
-       GSource                 source;
-       int                     fd;
-       struct termios          save_termios;
-       struct aoview_buf       in_buf;
-       struct aoview_buf       out_buf;
-       GPollFD                 poll_fd;
-};
-
-
-void
-aoview_serial_printf(struct aoview_serial *serial, char *format, ...)
-{
-       char    buf[1024];
-       va_list ap;
-       int     ret;
-
-       /* sprintf to a local buffer */
-       va_start(ap, format);
-       ret = vsnprintf(buf, sizeof(buf), format, ap);
-       va_end(ap);
-       if (ret > sizeof(buf)) {
-               fprintf(stderr, "printf overflow for format %s\n",
-                       format);
-       }
-
-       /* flush local buffer to the wire */
-       aoview_buf_write(&serial->out_buf, buf, ret);
-       aoview_buf_flush(&serial->out_buf, serial->fd);
-}
-
-int
-aoview_serial_read(struct aoview_serial *serial, char *buf, int len)
-{
-       return aoview_buf_read(&serial->in_buf, buf, len);
-}
-
-int
-aoview_serial_getc(struct aoview_serial *serial)
-{
-       return aoview_buf_getc(&serial->in_buf);
-}
-
-static gboolean
-serial_prepare(GSource *source, gint *timeout)
-{
-       struct aoview_serial *serial = (struct aoview_serial *) source;
-       *timeout = -1;
-
-       if (serial->out_buf.count)
-               serial->poll_fd.events |= G_IO_OUT;
-       else
-               serial->poll_fd.events &= ~G_IO_OUT;
-       return FALSE;
-}
-
-static gboolean
-serial_check(GSource *source)
-{
-       struct aoview_serial *serial = (struct aoview_serial *) source;
-       gint revents = serial->poll_fd.revents;
-
-       if (revents & G_IO_NVAL)
-               return FALSE;
-       if (revents & G_IO_IN)
-               return TRUE;
-       if (revents & G_IO_OUT)
-               return TRUE;
-       return FALSE;
-}
-
-static gboolean
-serial_dispatch(GSource *source,
-               GSourceFunc callback,
-               gpointer user_data)
-{
-       struct aoview_serial *serial = (struct aoview_serial *) source;
-       aoview_serial_callback func = (aoview_serial_callback) callback;
-       gint revents = serial->poll_fd.revents;
-
-       if (revents & G_IO_IN)
-               aoview_buf_fill(&serial->in_buf, serial->fd);
-
-       if (revents & G_IO_OUT)
-               aoview_buf_flush(&serial->out_buf, serial->fd);
-
-       if (func)
-               (*func)(user_data, serial, revents);
-       return TRUE;
-}
-
-static void
-serial_finalize(GSource *source)
-{
-       struct aoview_serial *serial = (struct aoview_serial *) source;
-
-       aoview_buf_fini(&serial->in_buf);
-       aoview_buf_fini(&serial->out_buf);
-       tcsetattr(serial->fd, TCSAFLUSH, &serial->save_termios);
-       close (serial->fd);
-}
-
-static GSourceFuncs serial_funcs = {
-       serial_prepare,
-       serial_check,
-       serial_dispatch,
-       serial_finalize
-};
-
-struct aoview_serial *
-aoview_serial_open(const char *tty)
-{
-       struct aoview_serial    *serial;
-       struct termios  termios;
-
-       serial = (struct aoview_serial *) g_source_new(&serial_funcs, sizeof (struct aoview_serial));
-       aoview_buf_init(&serial->in_buf);
-       aoview_buf_init(&serial->out_buf);
-       serial->fd = open (tty, O_RDWR | O_NONBLOCK);
-       if (serial->fd < 0) {
-               g_source_destroy(&serial->source);
-               return NULL;
-       }
-       tcgetattr(serial->fd, &termios);
-       serial->save_termios = termios;
-       cfmakeraw(&termios);
-       tcsetattr(serial->fd, TCSAFLUSH, &termios);
-
-       aoview_serial_printf(serial, "E 0\n");
-       tcdrain(serial->fd);
-       usleep(15*1000);
-       tcflush(serial->fd, TCIFLUSH);
-       serial->poll_fd.fd = serial->fd;
-       serial->poll_fd.events = G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR;
-       g_source_attach(&serial->source, NULL);
-       g_source_add_poll(&serial->source,&serial->poll_fd);
-       aoview_serial_set_callback(serial, NULL);
-       return serial;
-}
-
-void
-aoview_serial_close(struct aoview_serial *serial)
-{
-       g_source_remove_poll(&serial->source, &serial->poll_fd);
-       close(serial->fd);
-       g_source_destroy(&serial->source);
-}
-
-void
-aoview_serial_set_callback(struct aoview_serial *serial,
-                          aoview_serial_callback func)
-{
-       g_source_set_callback(&serial->source, (GSourceFunc) func, serial, NULL);
-}
diff --git a/ao-view/aoview_state.c b/ao-view/aoview_state.c
deleted file mode 100644 (file)
index 7efd33b..0000000
+++ /dev/null
@@ -1,349 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-#include <math.h>
-
-static inline double sqr(double a) { return a * a; };
-
-static void
-aoview_great_circle (double start_lat, double start_lon,
-                    double end_lat, double end_lon,
-                    double *dist, double *bearing)
-{
-       const double rad = M_PI / 180;
-       const double earth_radius = 6371.2 * 1000;      /* in meters */
-       double lat1 = rad * start_lat;
-       double lon1 = rad * -start_lon;
-       double lat2 = rad * end_lat;
-       double lon2 = rad * -end_lon;
-
-       double d_lat = lat2 - lat1;
-       double d_lon = lon2 - lon1;
-
-       /* From http://en.wikipedia.org/wiki/Great-circle_distance */
-       double vdn = sqrt(sqr(cos(lat2) * sin(d_lon)) +
-                         sqr(cos(lat1) * sin(lat2) -
-                             sin(lat1) * cos(lat2) * cos(d_lon)));
-       double vdd = sin(lat1) * sin(lat2) + cos(lat1) * cos(lat2) * cos(d_lon);
-       double d = atan2(vdn,vdd);
-       double course;
-
-       if (cos(lat1) < 1e-20) {
-               if (lat1 > 0)
-                       course = M_PI;
-               else
-                       course = -M_PI;
-       } else {
-               if (d < 1e-10)
-                       course = 0;
-               else
-                       course = acos((sin(lat2)-sin(lat1)*cos(d)) /
-                                     (sin(d)*cos(lat1)));
-               if (sin(lon2-lon1) > 0)
-                       course = 2 * M_PI-course;
-       }
-       *dist = d * earth_radius;
-       *bearing = course * 180/M_PI;
-}
-
-static void
-aoview_state_add_deg(int column, char *label, double deg, char pos, char neg)
-{
-       double  int_part;
-       double  min;
-       char    sign = pos;
-
-       if (deg < 0) {
-               deg = -deg;
-               sign = neg;
-       }
-       int_part = floor (deg);
-       min = (deg - int_part) * 60.0;
-       aoview_table_add_row(column, label, "%d°%lf'%c",
-                            (int) int_part, min, sign);
-
-}
-
-static char *ascent_states[] = {
-       "boost",
-       "fast",
-       "coast",
-       0,
-};
-
-static double
-aoview_time(void)
-{
-       struct timespec now;
-
-       clock_gettime(CLOCK_MONOTONIC, &now);
-       return (double) now.tv_sec + (double) now.tv_nsec / 1.0e9;
-}
-
-/*
- * Fill out the derived data fields
- */
-static void
-aoview_state_derive(struct aodata *data, struct aostate *state)
-{
-       int     i;
-       double  new_height;
-       double  height_change;
-       double  time_change;
-       int     tick_count;
-
-       state->report_time = aoview_time();
-
-       state->prev_data = state->data;
-       state->prev_npad = state->npad;
-       state->data = *data;
-       tick_count = data->tick;
-       if (tick_count < state->prev_data.tick)
-               tick_count += 65536;
-       time_change = (tick_count - state->prev_data.tick) / 100.0;
-
-       state->ground_altitude = aoview_pres_to_altitude(data->ground_pres);
-       new_height = aoview_pres_to_altitude(data->flight_pres) - state->ground_altitude;
-       height_change = new_height - state->height;
-       state->height = new_height;
-       if (time_change)
-               state->baro_speed = (state->baro_speed * 3 + (height_change / time_change)) / 4.0;
-       state->acceleration = (data->ground_accel - data->flight_accel) / 27.0;
-       state->speed = data->flight_vel / 2700.0;
-       state->temperature = ((data->temp / 32767.0 * 3.3) - 0.5) / 0.01;
-       state->drogue_sense = data->drogue / 32767.0 * 15.0;
-       state->main_sense = data->main / 32767.0 * 15.0;
-       state->battery = data->batt / 32767.0 * 5.0;
-       if (!strcmp(data->state, "pad")) {
-               if (data->gps.gps_locked && data->gps.nsat >= 4) {
-                       state->npad++;
-                       state->pad_lat_total += data->gps.lat;
-                       state->pad_lon_total += data->gps.lon;
-                       state->pad_alt_total += data->gps.alt;
-                       if (state->npad > 1) {
-                               state->pad_lat = (state->pad_lat * 31 + data->gps.lat) / 32.0;
-                               state->pad_lon = (state->pad_lon * 31 + data->gps.lon) / 32.0;
-                               state->pad_alt = (state->pad_alt * 31 + data->gps.alt) / 32.0;
-                       } else {
-                               state->pad_lat = data->gps.lat;
-                               state->pad_lon = data->gps.lon;
-                               state->pad_alt = data->gps.alt;
-                       }
-               }
-       }
-       state->ascent = FALSE;
-       for (i = 0; ascent_states[i]; i++)
-               if (!strcmp(data->state, ascent_states[i]))
-                       state->ascent = TRUE;
-
-       /* Only look at accelerometer data on the way up */
-       if (state->ascent && state->acceleration > state->max_acceleration)
-               state->max_acceleration = state->acceleration;
-       if (state->ascent && state->speed > state->max_speed)
-               state->max_speed = state->speed;
-
-       if (state->height > state->max_height)
-               state->max_height = state->height;
-       state->gps.gps_locked = data->gps.gps_locked;
-       state->gps.gps_connected = data->gps.gps_connected;
-       if (data->gps.gps_locked) {
-               state->gps = data->gps;
-               state->gps_valid = 1;
-               if (state->npad)
-                       aoview_great_circle(state->pad_lat, state->pad_lon, state->gps.lat, state->gps.lon,
-                                           &state->distance, &state->bearing);
-       }
-       if (state->npad) {
-               state->gps_height = state->gps.alt - state->pad_alt;
-       } else {
-               state->gps_height = 0;
-       }
-}
-
-void
-aoview_speak_state(struct aostate *state)
-{
-       if (strcmp(state->data.state, state->prev_data.state)) {
-               aoview_voice_speak("%s\n", state->data.state);
-               if (!strcmp(state->data.state, "drogue"))
-                       aoview_voice_speak("apogee %d meters\n",
-                                          (int) state->max_height);
-               if (!strcmp(state->prev_data.state, "boost"))
-                       aoview_voice_speak("max speed %d meters per second\n",
-                                          (int) state->max_speed);
-       }
-       if (state->prev_npad < MIN_PAD_SAMPLES && state->npad >= MIN_PAD_SAMPLES)
-               aoview_voice_speak("g p s ready\n");
-}
-
-void
-aoview_speak_height(struct aostate *state)
-{
-       aoview_voice_speak("%d meters\n", state->height);
-}
-
-struct aostate aostate;
-
-static guint aostate_timeout;
-
-#define COMPASS_LIMIT(n)       ((n * 22.5) + 22.5/2)
-
-static char *compass_points[] = {
-       "north",
-       "north north east",
-       "north east",
-       "east north east",
-       "east",
-       "east south east",
-       "south east",
-       "south south east",
-       "south",
-       "south south west",
-       "south west",
-       "west south west",
-       "west",
-       "west north west",
-       "north west",
-       "north north west",
-};
-
-static char *
-aoview_compass_point(double bearing)
-{
-       int     i;
-       while (bearing < 0)
-               bearing += 360.0;
-       while (bearing >= 360.0)
-               bearing -= 360.0;
-
-       i = floor ((bearing - 22.5/2) / 22.5 + 0.5);
-       if (i < 0) i = 0;
-       if (i >= sizeof (compass_points) / sizeof (compass_points[0]))
-               i = 0;
-       return compass_points[i];
-}
-
-static gboolean
-aoview_state_timeout(gpointer data)
-{
-       double  now = aoview_time();
-
-       if (strlen(aostate.data.state) > 0 && strcmp(aostate.data.state, "pad") != 0)
-               aoview_speak_height(&aostate);
-       if (now - aostate.report_time >= 20 || !strcmp(aostate.data.state, "landed")) {
-               if (!aostate.ascent) {
-                       if (fabs(aostate.baro_speed) < 20 && aostate.height < 100)
-                               aoview_voice_speak("rocket landed safely\n");
-                       else
-                               aoview_voice_speak("rocket may have crashed\n");
-                       if (aostate.gps_valid) {
-                               aoview_voice_speak("rocket reported %s of pad distance %d meters\n",
-                                                  aoview_compass_point(aostate.bearing),
-                                                  (int) aostate.distance);
-                       }
-               }
-               aostate_timeout = 0;
-               return FALSE;
-       }
-       return TRUE;
-}
-
-void
-aoview_state_reset(void)
-{
-       memset(&aostate, '\0', sizeof (aostate));
-}
-
-void
-aoview_state_notify(struct aodata *data)
-{
-       struct aostate *state = &aostate;
-       aoview_state_derive(data, state);
-       aoview_table_start();
-
-       if (state->npad >= MIN_PAD_SAMPLES)
-               aoview_table_add_row(0, "Ground state", "ready");
-       else
-               aoview_table_add_row(0, "Ground state", "waiting for gps (%d)",
-                                    MIN_PAD_SAMPLES - state->npad);
-       aoview_table_add_row(0, "Rocket state", "%s", state->data.state);
-       aoview_table_add_row(0, "Callsign", "%s", state->data.callsign);
-       aoview_table_add_row(0, "Rocket serial", "%d", state->data.serial);
-
-       aoview_table_add_row(0, "RSSI", "%6ddBm", state->data.rssi);
-       aoview_table_add_row(0, "Height", "%6dm", state->height);
-       aoview_table_add_row(0, "Max height", "%6dm", state->max_height);
-       aoview_table_add_row(0, "Acceleration", "%7.1fm/s²", state->acceleration);
-       aoview_table_add_row(0, "Max acceleration", "%7.1fm/s²", state->max_acceleration);
-       aoview_table_add_row(0, "Speed", "%7.1fm/s", state->ascent ? state->speed : state->baro_speed);
-       aoview_table_add_row(0, "Max Speed", "%7.1fm/s", state->max_speed);
-       aoview_table_add_row(0, "Temperature", "%6.2f°C", state->temperature);
-       aoview_table_add_row(0, "Battery", "%5.2fV", state->battery);
-       aoview_table_add_row(0, "Drogue", "%5.2fV", state->drogue_sense);
-       aoview_table_add_row(0, "Main", "%5.2fV", state->main_sense);
-       aoview_table_add_row(0, "Pad altitude", "%dm", state->ground_altitude);
-       aoview_table_add_row(1, "Satellites", "%d", state->gps.nsat);
-       if (state->gps.gps_locked) {
-               aoview_table_add_row(1, "GPS", "locked");
-       } else if (state->gps.gps_connected) {
-               aoview_table_add_row(1, "GPS", "unlocked");
-       } else {
-               aoview_table_add_row(1, "GPS", "not available");
-       }
-       if (state->gps_valid) {
-               aoview_state_add_deg(1, "Latitude", state->gps.lat, 'N', 'S');
-               aoview_state_add_deg(1, "Longitude", state->gps.lon, 'E', 'W');
-               aoview_table_add_row(1, "GPS height", "%d", state->gps_height);
-               aoview_table_add_row(1, "GPS time", "%02d:%02d:%02d",
-                                    state->gps.gps_time.hour,
-                                    state->gps.gps_time.minute,
-                                    state->gps.gps_time.second);
-       }
-       if (state->gps.gps_extended) {
-               aoview_table_add_row(1, "GPS ground speed", "%7.1fm/s %d°",
-                                    state->gps.ground_speed,
-                                    state->gps.course);
-               aoview_table_add_row(1, "GPS climb rate", "%7.1fm/s",
-                                    state->gps.climb_rate);
-               aoview_table_add_row(1, "GPS precision", "%4.1f(hdop) %3dm(h) %3dm(v)",
-                                    state->gps.hdop, state->gps.h_error, state->gps.v_error);
-       }
-       if (state->npad) {
-               aoview_table_add_row(1, "Distance from pad", "%5.0fm", state->distance);
-               aoview_table_add_row(1, "Direction from pad", "%4.0f°", state->bearing);
-               aoview_state_add_deg(1, "Pad latitude", state->pad_lat, 'N', 'S');
-               aoview_state_add_deg(1, "Pad longitude", state->pad_lon, 'E', 'W');
-               aoview_table_add_row(1, "Pad GPS alt", "%gm", state->pad_alt);
-       }
-       aoview_table_finish();
-       aoview_label_show(state);
-       aoview_speak_state(state);
-       if (!aostate_timeout && strcmp(state->data.state, "pad") != 0)
-               aostate_timeout = g_timeout_add_seconds(10, aoview_state_timeout, NULL);
-}
-
-void
-aoview_state_new(void)
-{
-}
-
-void
-aoview_state_init(GladeXML *xml)
-{
-       aoview_state_new();
-}
diff --git a/ao-view/aoview_table.c b/ao-view/aoview_table.c
deleted file mode 100644 (file)
index 9314300..0000000
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-#define NCOL   2
-
-static GtkTreeView     *dataview[NCOL];
-static GtkListStore    *datalist[NCOL];
-
-void
-aoview_table_start(void)
-{
-       int col;
-       for (col = 0; col < NCOL; col++)
-               datalist[col] = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
-}
-
-void
-aoview_table_add_row(int col, char *label, char *format, ...)
-{
-       char            buf[1024];
-       va_list         ap;
-       GtkTreeIter     iter;
-
-       va_start(ap, format);
-       vsnprintf(buf, sizeof (buf), format, ap);
-       va_end(ap);
-       gtk_list_store_append(datalist[col], &iter);
-       gtk_list_store_set(datalist[col], &iter,
-                          0, label,
-                          1, buf,
-                          -1);
-}
-
-void
-aoview_table_finish(void)
-{
-       int     col;
-       for (col = 0; col < NCOL; col++) {
-               gtk_tree_view_set_model(dataview[col], GTK_TREE_MODEL(datalist[col]));
-               g_object_unref(G_OBJECT(datalist[col]));
-               gtk_tree_view_columns_autosize(dataview[col]);
-       }
-}
-
-void
-aoview_table_clear(void)
-{
-       int     col;
-       for (col = 0; col < NCOL; col++)
-               gtk_tree_view_set_model(dataview[col], NULL);
-}
-
-void
-aoview_table_init(GladeXML *xml)
-{
-       int     col;
-
-       for (col = 0; col < NCOL; col++) {
-               char    name[32];
-               sprintf(name, "dataview_%d", col);
-               dataview[col] = GTK_TREE_VIEW(glade_xml_get_widget(xml, name));
-               assert(dataview[col]);
-
-               aoview_add_plain_text_column(dataview[col], "Field", 0, 20);
-               aoview_add_plain_text_column(dataview[col], "Value", 1, 32);
-       }
-}
diff --git a/ao-view/aoview_util.c b/ao-view/aoview_util.c
deleted file mode 100644 (file)
index 6ea62ac..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-char *
-aoview_fullname (char *dir, char *file)
-{
-       char    *new;
-       int     dlen = strlen (dir);
-       int     flen = strlen (file);
-       int     slen = 0;
-
-       if (dir[dlen-1] != '/')
-               slen = 1;
-       new = malloc (dlen + slen + flen + 1);
-       if (!new)
-               return 0;
-       strcpy(new, dir);
-       if (slen)
-               strcat (new, "/");
-       strcat(new, file);
-       return new;
-}
-
-char *
-aoview_basename(char *file)
-{
-       char *b;
-
-       b = strrchr(file, '/');
-       if (!b)
-               return file;
-       return b + 1;
-}
-
-int
-aoview_mkdir(char *dir)
-{
-       char    *slash;
-       char    *d;
-       char    *part;
-
-       d = dir;
-       for (;;) {
-               slash = strchr (d, '/');
-               if (!slash)
-                       slash = d + strlen(d);
-               if (!*slash)
-                       break;
-               part = strndup(dir, slash - dir);
-               if (!access(part, F_OK))
-                       if (mkdir(part, 0777) < 0)
-                               return -errno;
-               free(part);
-               d = slash + 1;
-       }
-       return 0;
-}
-
-GtkTreeViewColumn *
-aoview_add_plain_text_column (GtkTreeView *view, const gchar *title, gint model_column, gint width)
-{
-       GtkCellRenderer *renderer;
-       GtkTreeViewColumn *column;
-
-       renderer = gtk_cell_renderer_text_new ();
-       g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_NONE, NULL);
-       g_object_set(renderer, "width-chars", width, NULL);
-       column = gtk_tree_view_column_new_with_attributes (title, renderer,
-                                                          "text", model_column,
-                                                          NULL);
-       gtk_tree_view_column_set_resizable (column, FALSE);
-       gtk_tree_view_append_column (view, column);
-
-       return column;
-}
diff --git a/ao-view/aoview_voice.c b/ao-view/aoview_voice.c
deleted file mode 100644 (file)
index 24422df..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "aoview.h"
-
-#if HAVE_FLITE
-#include <stdarg.h>
-
-FILE   *aoview_flite;
-
-void aoview_voice_open(void)
-{
-       int     err;
-
-       if (!aoview_flite)
-               aoview_flite = aoview_flite_start();
-}
-
-void aoview_voice_close(void)
-{
-       if (aoview_flite) {
-               aoview_flite_stop();
-               aoview_flite = NULL;
-       }
-}
-
-void aoview_voice_speak(char *format, ...)
-{
-       va_list ap;
-
-       if (aoview_flite) {
-               va_start(ap, format);
-               vfprintf(aoview_flite, format, ap);
-               fflush(aoview_flite);
-               va_end(ap);
-       }
-}
-
-#else
-void aoview_voice_open(void)
-{
-}
-
-void aoview_voice_close(void)
-{
-}
-
-void aoview_voice_speak(char *format, ...)
-{
-}
-#endif
-
-
-static GtkCheckMenuItem        *voice_enable;
-
-#define ALTOS_VOICE_PATH       "/apps/aoview/voice"
-
-static void
-aoview_voice_enable(GtkWidget *widget, gpointer data)
-{
-       gboolean        enabled = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget));
-       GError          *error;
-       GConfClient     *gconf_client;
-
-       if (enabled) {
-               aoview_voice_open();
-               aoview_voice_speak("enable voice\n");
-       } else {
-               aoview_voice_speak("disable voice\n");
-               aoview_voice_close();
-       }
-       gconf_client = gconf_client_get_default();
-       gconf_client_set_bool(gconf_client,
-                             ALTOS_VOICE_PATH,
-                             enabled,
-                             &error);
-}
-
-void
-aoview_voice_init(GladeXML *xml)
-{
-       gboolean        enabled;
-       GConfClient     *gconf_client;
-
-       voice_enable = GTK_CHECK_MENU_ITEM(glade_xml_get_widget(xml, "voice_enable"));
-       assert(voice_enable);
-
-       gconf_client = gconf_client_get_default();
-       enabled = TRUE;
-       if (gconf_client)
-       {
-               GError  *error;
-
-               error = NULL;
-               enabled = gconf_client_get_bool(gconf_client,
-                                               ALTOS_VOICE_PATH,
-                                               &error);
-               if (error)
-                       enabled = TRUE;
-       }
-       gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(voice_enable), enabled);
-       if (enabled)
-               aoview_voice_open();
-
-       g_signal_connect(G_OBJECT(voice_enable), "toggled",
-                        G_CALLBACK(aoview_voice_enable),
-                        voice_enable);
-}
diff --git a/ao-view/design b/ao-view/design
deleted file mode 100644 (file)
index 6ec2ea7..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-Requirements:
-       real-time display of telemetry
-       off-line display of logged data
-       Logging of telemetry
-       Capture of logged data to disk
-
-Input data:
-       accelerometer
-       barometer
-       thermometer
-       gps
-       drogue and main continuity
-       battery voltage
-       time
-       reported flight state
-       reported events
-
-Computed data:
-       velocity (from accelerometer)
-       altitude
-       range
-       direction
-
-Displays:
-       numeric display of current rocket status
-       (graphics come later)
-       text message log
index b52bb6e9c52721b88622c6b8253e52a6ef0cc8b6..7bb0136b79678d8218dbad96e05a7d028c2b3d1d 100644 (file)
@@ -17,19 +17,98 @@ dnl  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 dnl
 dnl Process this file with autoconf to create configure.
 
-AC_INIT(COPYING)
-
-AM_INIT_AUTOMAKE(altos, 0.1)
+AC_PREREQ(2.57)
+AC_INIT([altos], 1.1)
+AC_CONFIG_SRCDIR([src/core/ao.h])
+AM_INIT_AUTOMAKE([foreign dist-bzip2])
 AM_MAINTAINER_MODE
 
+VERSION_DASH=`echo $VERSION | sed 's/\./-/g'`
+AC_SUBST(VERSION_DASH)
+
 dnl ==========================================================================
 
 AM_CONFIG_HEADER(config.h)
 
+AC_ARG_WITH(freetts, AS_HELP_STRING([--with-freetts=PATH],
+       [Set freetts class path (default /usr/share/java)]),
+       [FREETTS=$withval], [FREETTS=/usr/share/java])
+
+AC_SUBST(FREETTS)
+
+AC_ARG_WITH(jfreechart, AS_HELP_STRING([--with-jfreechart=PATH],
+       [Set jfreechart class path (default /usr/share/java)]),
+       [JFREECHART=$withval], [JFREECHART=/usr/share/java])
+
+AC_SUBST(JFREECHART)
+
+AC_ARG_WITH(jcommon, AS_HELP_STRING([--with-jcommon=PATH],
+       [Set jcommon class path (default /usr/share/java)]),
+       [JCOMMON=$withval], [JCOMMON=/usr/share/java])
+
+AC_SUBST(JCOMMON)
+
+AC_ARG_WITH(jvm, AS_HELP_STRING([--with-jvm-include=PATH],
+       [Set jvm include path for jni builds (default searches in /usr/lib/jvm)]),
+       [JVM_INCLUDE=$withval], [JVM_INCLUDE=auto])
+
+if test "x$JVM_INCLUDE" = "xauto"; then
+       AC_MSG_CHECKING([JVM include files])
+       for jvm in default-java java-6-openjdk java-6-sun; do
+               if test "x$JVM_INCLUDE" = "xauto"; then
+                       INCLUDE="/usr/lib/jvm/$jvm/include"
+                       if test -f "$INCLUDE"/jni.h; then
+                               JVM_INCLUDE="$INCLUDE"
+                       fi
+               fi
+       done
+       if test "x$JVM_INCLUDE" = "xauto"; then
+               AC_MSG_ERROR([no JVM include files found])
+       fi
+       AC_MSG_RESULT([$JVM_INCLUDE])
+fi
+
+AC_SUBST(JVM_INCLUDE)
+
+AC_ARG_WITH(android, AS_HELP_STRING([--with-android=PATH],
+       [Set android SDK path (default searches in a variety of places)]),
+       [ANDROID_SDK=$withval], [ANDROID_SDK=auto])
+
+echo ANDROID_SDK set to $ANDROID_SDK
+
+if test "x$ANDROID_SDK" = "xauto"; then
+       AC_MSG_CHECKING([Android SDK files])
+       for sdk in ../android/android-sdk-linux ../android/android-sdk ../android-sdk ../android-sdk-linux $HOME/android; do
+               if test "x$ANDROID_SDK" = "xauto"; then
+                       SDK="$sdk"
+                       if test -f "$SDK/SDK Readme.txt"; then
+                               ANDROID_SDK=`readlink -m "$SDK"`
+                       fi
+               fi
+       done
+       if test "x$ANDROID_SDK" = "xauto"; then
+               AC_MSG_NOTICE([no Android SDK found])
+               ANDROID_SDK=no
+       fi
+       AC_MSG_RESULT([$ANDROID_SDK])
+fi
+
+AM_CONDITIONAL([ANDROID], [test x$ANDROID_SDK != xno])
+
+AC_SUBST(ANDROID_SDK)
+
+AC_ARG_WITH(fat-dir, AS_HELP_STRING([--with-fat-dir=PATH],
+           [Set the directory to install the 'fat' distribution files to (defaults to not installing)]),
+           [FATDIR=$withval], [FATDIR=none])
+
+AM_CONDITIONAL(FATINSTALL, [test "x$FATDIR" != "xnone"])
+
+AC_SUBST(FATDIR)
+
 AC_PROG_CC
 AC_PROG_INSTALL
 AC_PROG_LN_S
-AC_PROG_RANLIB
+AC_PROG_LIBTOOL
 PKG_PROG_PKG_CONFIG
 
 CFLAGS="-g"
@@ -43,16 +122,6 @@ if test "x$GCC" = "xyes"; then
 fi
 AC_SUBST(WARN_CFLAGS)
 
-AC_CHECK_HEADERS(flite/flite.h,HAVE_FLITE_H=yes,HAVE_FLITE_H=no)
-AC_CHECK_LIB(flite, flite_init,HAVE_LIBFLITE=yes,HAVE_LIBFLITE=no,-lm)
-
-if test "x$HAVE_FLITE_H" = "xyes" -a "x$HAVE_LIBFLITE" = "xyes"; then
-       AC_DEFINE(HAVE_FLITE,1,[Define if the flite library is usable])
-       AC_SUBST(FLITE_LIBS,"-lflite_cmu_us_kal16 -lflite_usenglish -lflite_cmulex -lflite -lm")
-       AC_SUBST(FLITE_INCS,-Iflite)
-fi
-AM_CONDITIONAL(USE_FLITE,test "x$HAVE_FLITE_H" = "xyes" -a "x$HAVE_LIBFLITE" = "xyes")
-
 AC_CHECK_PROG([HAVE_SDCC], [sdcc], yes, no)
 if test "x$HAVE_SDCC" = "xno"; then
        AC_MSG_ERROR([Please install sdcc to build AltOs])
@@ -65,21 +134,35 @@ fi
 
 AC_CHECK_LIB(readline, readline)
 
-PKG_CHECK_MODULES([GNOME], [gtk+-2.0 libglade-2.0 gconf-2.0])
-
 PKG_CHECK_MODULES([LIBUSB], [libusb-1.0])
 
-PKG_CHECK_MODULES([ALSA], [alsa])
+AC_CHECK_HEADERS(libelf.h libelf/libelf.h, [break])
+AC_CHECK_HEADERS(gelf.h libelf/gelf.h, [break])
+
+PKG_CHECK_MODULES([LIBSTLINK], [stlink], [HAVE_STLINK=yes], [HAVE_STLINK=no])
+
+AM_CONDITIONAL([LIBSTLINK], [test x$HAVE_STLINK != xno])
 
 AC_OUTPUT([
 Makefile
-ao-view/Makefile
+altoslib/Makefile
+altosui/Makefile
+altosui/AltosVersion.java
+altosui/Info.plist
+altosui/libaltos/Makefile
+altosdroid/Makefile
+altosdroid/local.properties
 ao-tools/Makefile
 ao-tools/lib/Makefile
 ao-tools/ao-rawload/Makefile
 ao-tools/ao-dbg/Makefile
 ao-tools/ao-bitbang/Makefile
 ao-tools/ao-eeprom/Makefile
+ao-tools/ao-list/Makefile
 ao-tools/ao-load/Makefile
+ao-tools/ao-telem/Makefile
+ao-tools/ao-stmload/Makefile
+ao-tools/ao-send-telem/Makefile
 ao-utils/Makefile
+src/Version
 ])
diff --git a/contrib/arch-linux/PKGBUILD-git.altos b/contrib/arch-linux/PKGBUILD-git.altos
new file mode 100644 (file)
index 0000000..4210bc3
--- /dev/null
@@ -0,0 +1,102 @@
+# Original contributor: Bob Finch <w9ya@qrpqrci.net>
+pkgname=altos-git
+pkgver=20101125
+pkgrel=1
+pkgdesc="Software solutions for high powered rocketry avionics"
+arch=('i686' 'x86_64')
+url="http://www.altusmetrum.org/AltOS/"
+license=('GPL')
+provides=('altos')
+conflicts=('altos')
+depends=('openssl>=1.0.0' 'libusb1' 'plplot-svn' 'nickle' 'flite' 'kernel26>=2.6.33' 'fop'\
+         'gconf' 'alsa-oss' 'swig' 'libglade' 'freetts' 'jdk>=6u21' 'jfreechart' 'jcommon')
+makedepends=('git' 'docbook-xsl=1.76.0' 'sdcc=2.9.0-2_patched' 'nsis' 'tar' 'gzip' 'zip')
+optdepends=('uucp: cu is included & is a bare boned terminal 2 serial program'
+            'cutemon: gui-based minimal terminal to serial program'
+            'google-earth: useful for viewing the kml files of the flight path')
+options=('docs')
+
+_gitroot="git://git.gag.com/fw/altos"
+_gitname="altos"
+_pkgname="altosui"
+_libname="libaltos"
+
+build() {
+  cd "$srcdir"
+  msg "Connecting to GIT server...."
+  
+  if [ -d $_gitname ] ; then
+    cd $_gitname && git pull origin
+    msg "The local files are updated."
+  else
+    git clone $_gitroot
+  fi
+  
+  msg "GIT checkout done or server timeout"
+  msg "Starting make..."
+  
+  rm -rf "$srcdir/$_gitname-build"
+  git clone "$srcdir/$_gitname" "$srcdir/$_gitname-build"
+  
+  #
+  # BUILD HERE
+  #
+  
+  sh /etc/profile.d/jdk.sh
+  
+  cd "$srcdir/$_gitname-build"
+  ./autogen.sh --prefix=/usr --with-jvm=/opt/java/include/\
+   --with-freetts=/usr/share/java/freetts/lib --with-jcommon=/usr/share/java/jcommon/lib\
+   --with-jfreechart=/usr/share/java/jfreechart/lib\
+  # --with-fat-dir=$srcdir
+  # Use this ^ for placing all 3 "fat" packages (linux,mac,windows) into a numbered dir.
+  
+  cd "$srcdir/$_gitname-build/$_pkgname/"
+  sed -i 's:\$(datadir)/java:\$(datadir)/java/altos:' Makefile
+  # This ^ places altosui.jar in an 'altos' subdirectory.
+  sed -i 's:\${exec_prefix}/bin:\${exec_prefix}/share/java/altos:' Makefile
+  # This ^ relocates altosui to allow for an aoss wrapper script.
+  sed -i 's|:/usr/share/java/\*|:/usr/share/java/*:$(JCOMMON)/*:$(JFREECHART)/*|' Makefile
+  # This ^ fixes compilation to include reference to jcommon.jar .
+  sed -i 's:cp -p $(JFREECHART_CLASS):cp -p $(JFREECHART_CLASS) $(JCOMMON_CLASS):' Makefile
+  # This ^ includes jcommon.jar in the macosx fat package.
+  
+  cd "$srcdir/$_gitname-build/$_pkgname/$_libname/"
+  sed -i 's:$(JVM_INCLUDE):/opt/java/include\ -I/opt/java/include/linux:' Makefile
+  # This ^ enables both jni.h and jni_md.h to be found as required.
+  
+  cd "$srcdir/$_gitname-build/doc"
+  sed -i 's:stylesheet/docbook-xsl:xsl-stylesheets-1.76.0:' Makefile
+  # This ^ fixes archlinux's use of a specfic reference.
+  
+  cd "$srcdir/$_gitname-build"
+  make || return 1
+  
+  cd "$srcdir/$_gitname-build/doc"
+  make all
+  
+  cd "$srcdir/$_gitname-build/$_pkgname"
+  make fat
+}
+
+package() {
+  cd "$srcdir/$_gitname-build"
+  make DESTDIR="$pkgdir/" install
+  
+  mkdir -p $startdir/pkg/usr/share/altos
+  install -m 644 src/telemetrum-v1.0-* $startdir/pkg/usr/share/altos/
+  install -m 644 src/teledongle-v0.2-* $startdir/pkg/usr/share/altos/
+  
+  mkdir -p $startdir/pkg/usr/share/pixmaps
+  mkdir -p $startdir/pkg/usr/share/applications
+  install -m644 debian/*.xpm $startdir/pkg/usr/share/pixmaps
+  install -m644 debian/*.desktop $startdir/pkg/usr/share/applications
+  
+  echo "#!/bin/sh" > holding.bin
+  echo ". aoss /usr/share/java/altos/altosui" >> holding.bin
+  install -D -m 755 holding.bin $startdir/pkg/usr/bin/$_pkgname
+  
+  mkdir -p $startdir/pkg/usr/share/doc/altos
+  install -m 644 doc/*.html $startdir/pkg/usr/share/doc/altos/
+  install -m 644 doc/*.pdf $startdir/pkg/usr/share/doc/altos/
+}
diff --git a/contrib/arch-linux/PKGBUILD-git.freetts b/contrib/arch-linux/PKGBUILD-git.freetts
new file mode 100644 (file)
index 0000000..b16a994
--- /dev/null
@@ -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 (file)
index 0000000..8c43543
--- /dev/null
@@ -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 (file)
index 0000000..f742798
--- /dev/null
@@ -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.nickle b/contrib/arch-linux/PKGBUILD-git.nickle
new file mode 100644 (file)
index 0000000..fe6c2a7
--- /dev/null
@@ -0,0 +1,46 @@
+# Original contributor: Bob Finch <w9ya@qrpqrci.net>
+pkgname=nickle-git
+pkgver=20100518
+pkgrel=1
+pkgdesc="A desk calculator language with powerful programming and scripting capabilities."
+arch=('i686' 'x86_64')
+url="http://keithp.com/git-repository/"
+license=('custom')
+provides=('nickle')
+conflicts=('nickle')
+depends=('readline')
+makedepends=('git')
+
+_gitroot="git://keithp.com/git/nickle"
+_gitname="nickle"
+
+build() {
+  cd "$srcdir"
+  msg "Connecting to GIT server...."
+
+  if [ -d $_gitname ] ; then
+    cd $_gitname && git pull origin
+    msg "The local files are updated."
+  else
+    git clone $_gitroot
+  fi
+
+  msg "GIT checkout done or server timeout"
+  msg "Starting make..."
+
+  rm -rf "$srcdir/$_gitname-build"
+  git clone "$srcdir/$_gitname" "$srcdir/$_gitname-build"
+  cd "$srcdir/$_gitname-build"
+
+  #
+  # BUILD HERE
+  #
+
+  ./autogen.sh
+  ./configure --prefix=/usr
+  make || return 1
+  make DESTDIR="$pkgdir/" install
+  
+  install -D -m 644 COPYING\
+     $startdir/pkg/usr/share/licenses/$_gitname/COPYING.txt
+} 
diff --git a/contrib/arch-linux/PKGBUILD-git.nsis.patched b/contrib/arch-linux/PKGBUILD-git.nsis.patched
new file mode 100644 (file)
index 0000000..fe751cb
--- /dev/null
@@ -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 (file)
index 0000000..1a78c29
--- /dev/null
@@ -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/README b/contrib/arch-linux/README
new file mode 100644 (file)
index 0000000..5282d4f
--- /dev/null
@@ -0,0 +1,5 @@
+These files were contributed by Bob Finch <w9ya@qrpqrci.net>, and demonstrate
+how to build packages for the Arch Linux distribution:
+
+       http://www.archlinux.org/
+
diff --git a/contrib/arch-linux/new.patch b/contrib/arch-linux/new.patch
new file mode 100644 (file)
index 0000000..74e1df0
--- /dev/null
@@ -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 (file)
index 0000000..342396d
--- /dev/null
@@ -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/debian/.gitignore b/debian/.gitignore
new file mode 100644 (file)
index 0000000..850d7c2
--- /dev/null
@@ -0,0 +1,6 @@
+altos.debhelper.log
+altos.postinst.debhelper
+altos.postrm.debhelper
+altos.substvars
+altos
+files
diff --git a/debian/altos.desktop b/debian/altos.desktop
new file mode 100644 (file)
index 0000000..88b99f9
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Type=Application
+Name=AltOS UI
+GenericName=Altus Metrum Ground Station
+Comment=View and log downlink data from Altus Metrum products
+Icon=/usr/share/pixmaps/altusmetrum.xpm
+Exec=/usr/bin/altosui %f
+Terminal=false
+MimeType=text/plain;
+Categories=Education;Electronics;Science;
index aac3544bbb95903b33565a51c33335b52273ae6c..884fdaa71ccd439d23293a92b789befdf1d3b2bd 100644 (file)
@@ -1,3 +1,10 @@
-debian/repository      etc/apt/sources.list.d/altos.list
+debian/altos.desktop   usr/share/applications
+debian/altusmetrum.xpm usr/share/pixmaps
 src/*.ihx              usr/share/altos
 src/*.map              usr/share/altos
+themes/background.png          usr/share/altos/themes
+themes/slim/panel.png          usr/share/slim/themes/altusmetrum
+themes/slim/slim.theme         usr/share/slim/themes/altusmetrum
+themes/gdm/altusmetrum.xml     /usr/share/gdm/themes/altusmetrum
+themes/gdm/GdmGreeterTheme.desktop     /usr/share/gdm/themes/altusmetrum
+themes/gdm/screenshot.png      /usr/share/gdm/themes/altusmetrum
diff --git a/debian/altos.postinst b/debian/altos.postinst
new file mode 100644 (file)
index 0000000..2ef9a68
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -e
+
+test "$1" = 'configure' || exit 0
+
+rm -f /etc/apt/sources.list.d/altos.list
+
+#DEBHELPER#
+
diff --git a/debian/altusmetrum.xpm b/debian/altusmetrum.xpm
new file mode 100644 (file)
index 0000000..037b42d
--- /dev/null
@@ -0,0 +1,175 @@
+/* XPM */
+static char *icon[] = {
+/* columns rows colors chars-per-pixel */
+"26 32 137 2",
+"   c #BB3D7F",
+".  c #BC3F7C",
+"X  c #BC3E7E",
+"o  c #BF4178",
+"O  c #BE4179",
+"+  c #BD407A",
+"@  c #BE407A",
+"#  c #D95F46",
+"$  c #D95F47",
+"%  c #D55B4B",
+"&  c #D75D49",
+"*  c #D65C4B",
+"=  c #D75C4B",
+"-  c #D55A4E",
+";  c #D85D49",
+":  c #D85E48",
+">  c #D85E49",
+",  c #CD525B",
+"<  c #CE525B",
+"1  c #CE535B",
+"2  c #CF5459",
+"3  c #CB505D",
+"4  c #CD515D",
+"5  c #CC515E",
+"6  c #CC505F",
+"7  c #D05654",
+"8  c #D15655",
+"9  c #D05556",
+"0  c #D05557",
+"q  c #D35851",
+"w  c #D35951",
+"e  c #D25852",
+"r  c #D25853",
+"t  c #D45950",
+"y  c #D05558",
+"u  c #DB6241",
+"i  c #DB6142",
+"p  c #DC6340",
+"a  c #D96044",
+"s  c #DA6044",
+"d  c #C64B66",
+"f  c #CB4F60",
+"g  c #CB4F61",
+"h  c #C94E63",
+"j  c #CA4E62",
+"k  c #C84C65",
+"l  c #C94D65",
+"z  c #C84C66",
+"x  c #C84C67",
+"c  c #C2466E",
+"v  c #C3476F",
+"b  c #C4476E",
+"n  c #C74B68",
+"m  c #C74A69",
+"M  c #C5496B",
+"N  c #C6496A",
+"B  c #C6496B",
+"V  c #C4486D",
+"C  c #CB5060",
+"Z  c #C04471",
+"A  c #C24571",
+"S  c #C34670",
+"D  c #C24671",
+"F  c #C14473",
+"G  c #C24572",
+"H  c #C04375",
+"J  c #C14474",
+"K  c #B53885",
+"L  c #B83B81",
+"P  c #BA3C80",
+"I  c #BA3C81",
+"U  c #B83985",
+"Y  c #B83A85",
+"T  c #B63789",
+"R  c #B4358B",
+"E  c #B5378A",
+"W  c #B3348E",
+"Q  c #B3348F",
+"!  c #B4358D",
+"~  c #B63888",
+"^  c #AF2F96",
+"/  c #AE2F97",
+"(  c #AB2C99",
+")  c #A82A9D",
+"_  c #AB2B9E",
+"`  c #AC2C9C",
+"'  c #AF3095",
+"]  c #B23390",
+"[  c #B03095",
+"{  c #9E1DB2",
+"}  c #9F1EB2",
+"|  c #9B1BB5",
+" . c #9D1CB5",
+".. c #9A17BF",
+"X. c #9B19BB",
+"o. c #9C1ABA",
+"O. c #9A19BC",
+"+. c #9B19BC",
+"@. c #A01EB3",
+"#. c #A726A5",
+"$. c #A625A6",
+"%. c #A626A6",
+"&. c #A929A0",
+"*. c #A828A2",
+"=. c #A423AB",
+"-. c #A524A8",
+";. c #A524AA",
+":. c #A221AE",
+">. c #8E0CCD",
+",. c #910FCB",
+"<. c #900DCE",
+"1. c #9413C4",
+"2. c #9613C6",
+"3. c #9815C2",
+"4. c #9310CA",
+"5. c #9512C8",
+"6. c #8F0CD2",
+"7. c #8E0BD4",
+"8. c #8D09D7",
+"9. c #8603DE",
+"0. c #8A05DD",
+"q. c #8C08D8",
+"w. c #8200E1",
+"e. c #8200E2",
+"r. c #8300E2",
+"t. c #8300E3",
+"y. c #8401E0",
+"u. c #8602E0",
+"i. c #8602E2",
+"p. c #8400E4",
+"a. c #8400E5",
+"s. c #8500E5",
+"d. c #8501E5",
+"f. c #8601E5",
+"g. c #8500E6",
+"h. c #8804E0",
+"j. c None",
+/* pixels */
+"j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.j.j.a.j.j.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.j.j.a.g.j.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.j.j.g.a.j.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.j.a.a.a.j.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.j.a.a.a.a.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.j.g.a.a.a.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.a.a.j.j.a.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.a.a.j.j.a.a.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.p.j.j.j.w.w.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.w.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.a.a.j.j.j.j.a.a.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.j.a.a.w.j.j.a.a.a.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.p.a.a.a.j.a.a.a.a.j.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.a.a.w.j.w.w.j.a.a.a.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.j.a.a.p.j.j.j.j.p.g.a.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.p.a.a.w.j.j.j.j.p.g.a.j.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.a.g.a.p.j.j.j.j.p.f.a.a.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.j.a.h.q.>.j.j.j.j.,.7.0.p.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.9.q.4.3. .j.j.j.j.{ X.2.6.j.j.j.j.j.j.j.",
+"j.j.j.j.j.j.<.3.@.#._ j.j.j.j.( *.:.X.2.j.j.j.j.j.j.",
+"j.j.j.j.j.j.O.;.` ! K j.j.j.j.I E [ #.@.j.j.j.j.j.j.",
+"j.j.j.j.j. .;.' Y o F j.j.j.j.v J P ] &.j.j.j.j.j.j.",
+"j.j.j.j.j.$.' P F M k j.j.j.j.k M v O ! ( j.j.j.j.j.",
+"j.j.j.j.j.' P F M j 3 j.j.j.j.1 6 z b O E j.j.j.j.j.",
+"j.j.j.j.j.~ F n C 2 9 j.j.j.j.w 9 6 z V X j.j.j.j.j.",
+"j.j.j.j.! O V C 2 w % j.j.j.j.% * 9 6 z F P j.j.j.j.",
+"j.j.j.j.K S z y t : $ j.j.j.j.p s - 9 j M O j.j.j.j.",
+"j.j.j./ + V C 9 * s j.j.j.j.j.j.p : t 1 z J E j.j.j.",
+"j.j.:.] J z 2 t : j.j.j.j.j.j.j.j.s * 9 j b I *.j.j.",
+"j.5.;.~ S z y t j.j.j.j.j.j.j.j.j.j.: r 6 M X _ X.j."
+};
index 9c5d1037eba76a885b9e7c8a5f82a47499ea6dfb..2aeaaa3ce9bff4129bb3e93ef1010f88a951da6d 100644 (file)
-altos (0.4+137+gea86b19) unstable; urgency=low
+altos (1.1-1) unstable; urgency=low
+
+  * new upstream minor release, which provides a few new features in AltosUI
+    and the AltOS firmware, and fixes bugs
+    - Add apogee-lockout value. Overrides the apogee detection logic to
+      prevent incorrect apogee charge firing.  A configuration menu 
+      provides a list of reasonable values, or the value can be set by hand.
+    - Fix a bug where the data reported in telemetry packets was from 
+      320ms ago.
+    - Force the radio frequency to 434.550MHz when the debug clock
+      pin is connected to ground at boot time. This provides a way
+      to talk to a TeleMini which is configured to some unknown frequency.
+    - Provide RSSI values for Monitor Idle mode. This makes it easy to check 
+      radio range without needing to go to flight mode.
+    - Fix a bug which caused the old received telemetry packets to
+      be retransmitted over the USB link when the radio was turned
+      off and back on.
+    - Fix a bug that caused GPS ready to happen too quickly. The
+      software was using every telemetry packet to signal new GPS
+      data, which caused GPS ready to be signalled after 10 packets
+      instead of 10 GPS updates.
+    - Fix Google Earth data export to work with recent versions. The
+      google earth file loading code got a lot pickier, requiring
+      some minor white space changes in the export code.
+    - Make the look-n-feel configurable, providing a choice from
+      the available options.
+    - Add an 'Age' element to mark how long since a telemetry packet
+      has been received. Useful to quickly gauge whether
+      communications with the rocket are still active.
+    - Add 'Configure Ground Station' dialog to set the radio
+      frequency used by a particular TeleDongle without having to go
+      through the flight monitor UI.
+    - Re-compute time spent in each state for the flight graph; this
+      figures out the actual boost and landing times instead of
+      using the conservative values provide by the flight
+      electronics. This improves the accuracy of the boost
+      acceleration and main descent rate computations.
+    - Make AltosUI run on Mac OS Lion. The default Java heap space
+      was dramatically reduced for this release causing much of the
+      UI to fail randomly. This most often affected the satellite
+      mapping download and displays.
+    - Change how data are displayed in the 'table' tab of the flight
+      monitoring window. This eliminates entries duplicated from the
+      header and adds both current altitude and pad altitude, which
+      are useful in 'Monitor Idle' mode.
+    - Add Imperial units mode to present data in feet instead of
+  * update build-deps to use cc1111 intead of sdcc, closes: #676739
+
+ -- Bdale Garbee <bdale@gag.com>  Tue, 11 Sep 2012 18:20:32 -0600
+
+altos (1.0.3) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * lose the bluetooth lib dependency until it's relevant
+  * don't try to include bluetooth headers
+  * don't try to link bluetooth lib
+  * mention release notes in Releasing file prep stage
+  * more notes on release notes in Releasing
+  * releasing 1.0.3
+
+  [ Keith Packard ]
+  * altos: Create TeleMetrum v1.2 directory
+  * Add 1.0.3 release notes.
+
+ -- Bdale Garbee <bdale@gag.com>  Sun, 25 Dec 2011 19:08:56 -0700
+
+altos (1.0.2) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * update release process docs
+  * update changelogs for Debian build
+  * include 1.0.1 release notes in docs, closes: #642705
+  * add run-time dependency on libjfreechart-java
+
+  [ Keith Packard ]
+  * altos: Delay reboot by a second to avoid re-entering idle mode
+  * Bump to version 1.0.2
+  * altos: TM: Don't turn on packet slave mode until idle/invalid state
+  * doc: Add 1.0.2 release notes
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 28 Sep 2011 01:58:27 -0600
+
+altos (1.0.1) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * use multimaint-merge to make Debian changelogs less ugly
+  * update TeleMini turnon script now that we've made a stable firmware release
+  * moving git-buildpackage config into .git/ since it is fairly specific to 
+    Bdale's build environment, and doesn't need to be in the source package
+  * get ready for a 1.0.1 release
+  * update changelogs for Debian build
+  * fix telemini firmware path name
+  * ignore generated log file
+  * roll release notes version from 1.0 to 1.0.1
+  * roll back packaging changelog for rebuild
+
+  [ Keith Packard ]
+  * altos: Clear callsign on initial config load
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 26 Aug 2011 10:39:29 -0600
+
+altos (1.0) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altos: Lost change that reported flight 0 when log memory was full
+  * altosui: Ancient log files used 'apogee' for 'coast' state
+  * altosui: Add 'On-board Data Logging' indicator to pad tab
+
+  [ Bdale Garbee ]
+  * update changelogs for Debian build
+  * update changelogs for Debian build
+
+  [ Keith Packard ]
+  * altosui: remove debug printf in pad pane
+  * altosui: Disable 'max flight log' config when there are stored flights
+  * altos: Merge common config code in ao_config.c
+  * Bump version to 0.9.7
+  * altos/altosui: Add ability to disable telemetry/rdf completely
+  * altosui: Clean up command line processing. Add --graph
+  * altos-fat/windows: Check and install Java 1.6 as needed
+  * altosui/windows: Fix a bunch of windows compiler warnings.
+  * altosui: Attempt to make both 32- and 64-bit windows DLLs
+  * doc: Add note about telemetry disable mode to 1.0 release notes
+  * doc: Add Installation Recommendations chapter
+  * altosui: Add a few simple unit conversions
+  * altosui: Capture date/time/serial/flight in AltosFlightStats
+  * altosui: Add date/time/serial/flight to flight stats tab
+  * altosui: Show filename in AltosGraph window
+  * altosui: Add --summary option to dump flight stats to stdout
+  * altosui: Make monitor-idle display correct 'On-board data logging' status
+  * altosui: Can't configure flight log max on TeleMini
+  * altosui: fix 'magic' string to signal end of config data
+  * altosui: Only 'show' config dialog once
+  * altosui: Reset all config data on 'reset' command
+  * altosui: Update mac os X library
+  * libaltos: fix Mac OS X function signatures
+  * altosui: add tool-tips to the button box.
+
+  [ Bdale Garbee ]
+  * turn off auto-tagging during Debian build
+  * update changelogs for Debian build
+  * rewind changelog to 0.9.6.0 for rebuild
+  * update changelogs for Debian build
+
+  [ Keith Packard ]
+  * altosui: Use system look&feel
+  * Remove stale tools (ao-dumplog, ao-postflight, ao-view)
+  * altosui: Add tool-tips to config dialogs
+  * altosui: Try to get dialogs to look a little better
+
+  [ Bdale Garbee ]
+  * another test round
+  * roll changelog back in prep for another test build
+  * update changelogs for Debian build
+  * add pkg-config to the build deps
+  * prepare for another rebuild
+  * update changelogs for Debian build
+
+  [ Keith Packard ]
+  * altosui: Don't trust companion telemetry record 'channels' count
+  * altosui: Make flight monitor font size configurable
+  * altos/windows: Get latest JRE 1.6 version (Version 6 update 27)
+
+  [ Bdale Garbee ]
+  * doc tweaks through chap 3
+
+  [ Keith Packard ]
+  * doc: Spelling corrections in altusmetrum.xsl
+  * doc: Document pad-mode 'on-board data logging' indicator
+
+  [ Bdale Garbee ]
+  * more doc tweaking
+
+  [ Keith Packard ]
+  * doc: Describe packet command mode a bit better.
+  * doc: Move Packet Command Mode section to System Operations chapter
+
+  [ Bdale Garbee ]
+  * more doc tweaks
+  * more tweaks
+
+  [ Keith Packard ]
+  * doc: use 'radio link' to refer to packet command mode
+
+  [ Bdale Garbee ]
+  * more tweaking
+
+  [ Keith Packard ]
+  * doc: Move updating device firmware section to separate chapter
+  * doc: Updating Firmware is now a separate chapter
+
+  [ Bdale Garbee ]
+  * more changes
+
+  [ Keith Packard ]
+  * doc: Move the remaining command-mode descriptions to the appendix
+
+  [ Bdale Garbee ]
+  * tweak tweak tweak
+
+  [ Keith Packard ]
+  * doc: Describe max flight log, ignite mode and pad orientation
+  * doc: Remove duplicate documentation about max flight log
+
+  [ Bdale Garbee ]
+  * more tweaks
+  * prepare to release
+  * don't deliver sources.list fragment in official Debian packages
+  * changes in preparation for upload to Debian
+  * ao-view is no longer included in the altos package, so have the 
+    old-style Debian menu entry point to altosui instead
+  * rewind packaging changelog to last tagged version
+  * update desktop file for consistency with package section
+  * we need a main category in the desktop file
+  * really, I mean it, no longer deliver the sources.list fragment
+  * add a postinst to remove sources.list.d fragment delivered by old 
+    private versions of this package, no longer needed once we have 
+    official packages
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 25 Aug 2011 02:22:21 -0600
+
+altos (0.9.6.0) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altos: Correct flight log max on Tm to 5k
+  * altosui: Change button to 'Configure Altimeter'
+  * altos: Remove pad_orientation functions from non-accel devices
+  * altos: Add ability to set arbitrary radio frequency
+  * altosui: Add dialogs to configure 'common' frequencies
+  * altosui: Convert from channels to frequencies
+  * Set version to 0.9.5.0
+  * altosui: altimeter is not spelled altimter
+  * altosui: Pull out BlueTooth support
+  * altos: minor type in comment about accel correction
+  * altosui: Must set radio calibration before radio setting
+  * altosui: A few misc cleanups.
+  * altosui: Have single radio_to_frequency function
+  * altosui: Save frequency after setting it in AltosFlightUI
+  * altosui: Show AltosFrequency in scan results
+  * altosui: Remove debugging printfs from AltosSerial
+  * altosui: Flush radio setting to serial device
+  * altosui: Remove unused AltosConfigData from AltosTelemetryReader
+  * altosui: Reading serial from swing thread only bad if remote
+  * altosui: Make set of telemetries to use while scanning configurable
+  * altosui: Add close button to 'fire' dialog
+  * altos: Switch telemini from v0.1 to v1.0
+  * altosui: Don't export product defs from libaltos
+  * altosui: Ensure serial code tracks reply nesting correctly
+  * altosui: Ship TeleMini v1.0 firmware with fat blobs
+  * Altosui: Add flight statistics tab to graph window
+  * altosui: Add a 'Graph Flight' button to the 'landed'  tab
+  * altosui: Prune telemetry file graphs to just the flight
+  * altosui: Plot reasonable data from Tm files
+  * altosui: Move launch-sites.txt file to altusmetrum.org
+  * Bump version to 0.9.6.0
+  * doc: Update altusmetrum.xsl for v1.0 software and TeleMini
+  * doc: Document Ignite Mode and Pad Orientation configuration options
+  * doc: Describe 'stats' tab in Graph UI, 'Graph Flight' button.
+  * doc: Add telemetry docs to debian/linux/mac/windows packages
+  * doc: Add release notes, include them in altusmetrum doc. Shuffle altusmetrum
+  * doc: Add 1.0 release notes.
+  * altos: Pull igniter pins low as soon as possible at boot time
+  * altos: Apply igniter boot pulse-width reduction to telemini
+  * altos: remove monitor disable stubs from altimeter code
+  * ao-tools: ao-list was crashing with more than 3 devices connected
+  * libaltos: Mis-allocated device list in libaltos
+  * altos: shrink text space from ao_config.c
+  * altos: re-write a bit of GPS parsing code to reduce size
+  * altos/altosui: Report log format in the version command
+  * altos: Don't try to use non-basestations for remote eeprom download
+  * altos: Add SPI-based companion board support
+  * altos: Put SPI in slower mode when talking to companion board
+  * altos: Make sure companion task exits cleanly when done
+  * altos: add the 'L' command to show the status of a linked companion board
+  * altos: Check for companion init packet validity was busted
+  * altosui: Add companion support to the flight UI and CSV conversion
+  * altos: Send SPI message at flight state changes
+  * altos: Send serial/flight to companion board
+  * altos: wait 10s for companion to boot
+  * altosui: Clean up eeprom parsing a bit
+  * altosui: Add support for TeleScience eeprom download
+  * altosui: Devices with log-format can also delete flights
+  * altosui: Eliminate inter-chunk flush_input calls
+  * altos: AltosSerial.flush_input shouldn't discard Interrupted exceptions
+  * altos: Correct AO_CONFIG_MINOR from 6 to 7
+  * altos: Reset radio channel to zero when using radio setting
+  * altos: use raw height while waiting for landing
+  * altosui: don't set channel when using radio setting
+  * altosui: Respect storage limits in flight log max config
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 19 Aug 2011 22:47:25 -0600
+
+altos (0.9.4.5) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altosui: Mark empty eeprom records 'invalid', don't generate exception
+  * altosui: Always read whole eeprom block, even at end of flight
+  * altosui: Display eeprom parsing errors to user
+  * Revert "src/ao_gps_skytraq.c: Update logging rate to 10Hz"
+
+  [ Anthony Towns ]
+  * src/ao_cmd: Shave off bytes from doc strings
+
+  [ Keith Packard ]
+  * altos: Oops. Lost a couple of commands when merging the doc patch
+  * altosui: Missed jcommon.jar in the Mac OS install image
+  * Bump published version number to 0.9.1
+  * aoview: remove -s option.
+
+  [ Anthony Towns ]
+  * ao_intflash: Use internal flash for storage
+
+  [ Keith Packard ]
+  * altos: Make serial, usb, beeper and accelerometer optional components
+  * altos/test: Use ao_convert.c instead of hand-coded pres → alt func
+  * altos/test: Add baro-only flight test program
+  * altos: Start with packet slave running. Turn off in pad mode.
+  * altos: Add TeleMini v1.0
+
+  [ Anthony Towns ]
+  * ao_intflash: Avoid overwriting code
+
+  [ Keith Packard ]
+  * altos: Switch LED usage for TeleMini around
+  * altos: Switch pins around for TeleMini
+  * bringup: Add script for telemini
+  * ao-load: Make usb descriptor rewriting optional
+  * altos: oops -- altitude reporting wasn't pausing between signals
+  * altos: Internal flash ops block when running from flash
+
+  [ Anthony Towns ]
+  * ignore new flight test file
+
+  [ Keith Packard ]
+  * altos: Add tiny logging for TeleMini/TeleNano
+  * altos: Add kalman filters for baro-only boards
+  * altos: Tiny logging fixes. Scan at start, stop when land or full.
+  * altos: Add TeleNano support
+
+  [ Bdale Garbee ]
+  * fix up script to work and have reasonable texts
+
+  [ Keith Packard ]
+  * altos: Fix mini/nano default log size to available flash space
+  * altos/test: Add scripts to run lots of flights through the code
+  * altos: Write height values to log for nano/mini
+  * altos: Baro-only boards must not detect launch on accel or speed data
+  * altosui: Add software version to Configure AltosUI dialog
+  * altos: Don't init packet slave on TD. Make slave start optional
+  * altos: Configure packet size from send/recv parameters.
+  * altos: Make telemetry interval more consistent
+  * altos: Split out tiny telemetry from full telemetry
+  * altos: Split telenano main from telemini
+  * altos: The kalman code requires a constant sample rate
+  * altos: New telemetry report format (version 4). Supports tiny telemetry.
+  * altos: Add .sdcdbrc file for teledongle
+  * altos: Add nickle kalman implementation.
+  * altos: Switch telemetrum over to kalman filter
+  * altos: Compute a 'trust' value for the barometer
+  * altos/kalman: Kalman terms can be > 1, use 32-bit fixed point
+  * altos: Clean up some debug stuff in ao_flight.c
+  * altos: Add ao_flight_debug code
+  * altos: Missing parens and some bad arithmetic in the kalman code
+  * altos: Fix up flight code testing
+  * altos: Restore sensible kalman values
+  * altos: Ignore alt error for fast->coast. Allow larger error for baro apogee.
+  * altos: Exit flight test at landing. Allow description in test flight list
+  * Add ao_kalman.h to .gitignore
+  * Add description to test flights
+  * altosui: Add support for telemetry version 4
+  * altosui: Add telemetry format menu and preferences
+  * altosui: Remove a bunch of debug printfs from the eeprom manager code
+  * altosui: Add support for downloading TeleMini/TeleNano flight logs
+  * altosui: Remove extra AltosEepromBlock layer
+  * altosui: swing hide/show methods are deprecated
+  * altosui: Allow TM config connection to be canceled.
+  * altos: Variable log rate in full logging code too
+  * altos: full logging must flush pending data before checking state
+  * altosui: Off-by-one error in telemetry format configuration UI
+  * altosui: Allow radio channel to be configured over the radio link
+  * altosui: Tell serial device which frame to use for timeout dialogs
+  * altosui: Handle serial calls from swing thread
+  * altos: Split up flight code into separate flight/sample/kalman bits
+  * altos: Create custom nano flight code
+  * altosui: Clean up packet link connecting dialog
+  * altosui: Make flight log downloading handle 'Connecting...' dialog
+  * altosui: Make deployment testing handle Connecting... dialog
+  * altosui: Display exception messages from swing thread
+  * altosui: Don't display 0000-00-00 for missing flight log dates
+  * altos: ao_sample_preflight was exiting preflight mode immediately
+  * altos: Run RDF beacon after apogee instead of waiting for landing
+  * altos: Enable logging during nano flights
+  * altosui: Parse and export Max flight log value
+  * altosui: Only plot acceleration when present in data file
+  * altos: Reflect ao_flight split in ao_flight_test dependencies
+  * altos: Baro useful ceiling is MSL, not AGL
+  * altos: Make ao_flight_test show true height but report saturated height
+  * altos: Add initial TeleBT code
+  * altos: Add P2SEL_*_MASK defines to cc1111.h
+  * altos: expose set of available stdio values
+  * altos: Provide for a pre-filter on commands
+  * altos: Allow any stdio to be used with packet forwarding
+  * altos: Remove serial monitor command
+  * altos: Make ao_serial_drain public
+  * altos: Clean up usage of serial port for stdio
+  * altos: Clean up serial initialization
+  * altos: Make cmd echo per-connection instead of global
+  * altos: Clean up BT serial communcations
+  * altosui: Add missing AltosTelemetryMap.java file
+  * altos: Use PIO(6) on BTM to monitor BT connection. Fix BTM init.
+  * altosui: Add TeleBT USB device support
+  * altosui: Add low-level Bluetooth APIs
+  * altos/altosui: Log averaged baro sensor data in Tm/Tn
+  * altosui: oops - lost state changes when downloading eeprom data.
+  * altos: Write a few pre-launch samples for Tm/Tn devices
+  * altosui: Add primitive bluetooth device manager UI.
+  * altosui: Create abstract AltosDevice class
+  * altosui: Make AltosBTDevice implement AltosDevice interface
+  * altosui: Make bluetooth dialog modal
+  * altosui: Use persistent list of bluetooth devices for device dialogs
+  * altos: Add delays to bt startup sequence
+  * altosui: Wait two seconds after bluetooth connect XXX
+  * altos: Solidify BT connections
+  * altos: Simplify BT communications
+  * altos: Remove bt debug command
+  * altos: remove BT logging code
+  * altosui: Eliminate ao_cmd_filter hook
+  * altosui: Make flight data download work through TeleBT
+  * altos: add telebt-v0.0 Makefile
+  * altosui: Separate out flash debug code to separate thread
+  * altosui: Fix TeleBT name in flight monitor title
+  * altosui: Display reader name (usually the device) when an I/O error occurs
+  * altosui: Move AltosIgniteUI device open out of Swing thread
+  * altosui: Fix BT manage dialog so that the device lists resize
+  * altos: Add preliminary telebt v0.1 defines
+
+  [ Bdale Garbee ]
+  * first cut at a telebt turn on script
+
+  [ Keith Packard ]
+  * altos: Add telebt-v0.1 to Makefile
+  * altos: Use USART configuration 1 with flow control for TBT
+  * altos: Fix BT link status pin for real TBT hardware
+  * altos: pull TBT v0.1 ser_reset line low
+  * altos: Add beeper to TBT v0.1
+  * altos: Debugging TBT issues -- check pin configuration after boot
+  * altos: Initialize beeper for telebt
+  * altos: Hook up the P1 ISR for TeleBT v0.1 bt_link line
+  * altos: clear CPU port 1 interrupt flag when handled
+  * Revert "altos: Debugging TBT issues -- check pin configuration after boot"
+  * Switch version to 0.9.4
+  * altosui: Handle old TeleDongle receiving kalman telemetry packets
+  * altos: Rename telemetry to telemetry_orig
+  * altos: Add arbitrary telemetry packet monitoring
+  * altos: Add checksum to TELEM output lines
+  * altos: Start adding new telemetry frame definitions
+  * altos: ao_radio_recv needs byte count *including* rssi and status
+  * altosui: Support raw telemetry from TeleDongle
+  * doc: Add telemetry format description
+  * doc: Complete initial telemetry description
+  * doc: Fix a few minor telemetry doc mistakes
+  * Version strings must be < 8 bytes long
+  * altos: teledongle does not need ao_packet_slave.c
+  * altos: Shrink const space in ao_config
+  * altos: Add sat info to GPS report command
+  * altos: Shrink help text
+  * altos: Add split telemetry code
+  * altos: Complete new telemetry switchover
+  * altosui: Parse remaining standard telemetry packets
+  * doc: Chang Config and Location packets
+  * altos: Adapt to changes in telemetry Configuration packet
+  * doc: Add section about TeleDongle USB line format
+  * altosui: Elide nul bytes at end of telemetry string values
+  * altosui: Elide missing values from graphs
+  * altosui: Add main/drogue voltages to default graph
+  * altosui: Remove debug printf.
+  * altosui: Compress telemetry records marked with the same time
+  * altos: Shrink ao_cmd_put16, ao_cmd_hex and ao_cmd
+  * altos: Shrink ao_config_callsign_set
+  * altos: Shrink ao_add_task by rolling up a memset loop
+  * altos: Switch ao_gps_skytraq and ao_gps_sirf __xdata to __pdata
+  * altos: Switch ao_ignite and ao_gps_sirf __xdata to __pdata
+  * altos: Switch ao_log.c and ao_log_big.c __xdata to __pdata
+  * altos: Switch ao_report.c __xdata to __pdata
+  * altos: Switch ao_rssi.c __xdata to __pdata
+  * altos: Switch ao_sample.c __xdata to __pdata
+  * altos: Switch ao_serial.c __xdata to __pdata
+  * altos: Switch ao_stdio.c __data to __pdata
+  * altos: Switch ao_telemetry.c __xdata to __pdata
+  * altos: Switch ao_usb.c __xdata to __pdata
+  * altos: Switch const for __code in struct ao_cmds
+  * altos: switch ao_cmd __xdata to __pdata
+  * altos: Switch ao_config.c __xdata to __pdata
+  * altos: Switch ao_dbg.c __xdata to __pdata
+  * altos: Switch ao_flight and ao_flight_nano __xdata to __pdata
+  * altos: Switch flash drivers __xdata to __pdata
+  * altos: Switch Tm and Tn to common telemetry code
+  * altos: Remove ao_telemetry_orig.c and ao_telemetry_tiny.c
+  * altos: Ensure low-rate telem packets interleave with sensor telem packets
+  * altos: product defines are always in ao_product.h
+  * altosui: Build device constants into .java code
+  * altosui: Don't show missing igniter and gps values
+  * altos: new versions of sdcc require __ prefixes for custom keywords
+  * Set version to 0.9.4.3 for Bdale 2011-7-16 flights
+  * altosui: Start adding support for scanning radio for available devices
+  * altosui: Pop up monitor window from scan dialog
+  * altosui: Remove debugging printf from AltosLog
+  * altosui: Configuration telemetry record includes flight number
+  * altosui: Finish radio scanning UI
+  * altosui: Generalize and centralize telemetry constants, parse v0.8 telemetry
+  * altosui: Initialize channel and telemetry before use in ScanUI
+  * altosui: Set 'seen' bits in legacy telemetry packet reader
+  * altosui: Add map preloading GUI
+  * altosui: Try to avoid resize weirdness with map preloading
+  * altosui: Flush telemetry lines before starting to watch for scan results
+  * altosui: Display full map preload area in view.
+  * altosui: Remove a bunch of sitemap debugging printfs
+
+  [ Anthony Towns ]
+  * altosui: Make sure degree and minute values are visible (map preload)
+
+  [ Keith Packard ]
+  * Set version to 0.9.4.4
+  * altos, altosui: Add igniter mode (dual, apogee, main)
+  * altosui: Download list of site locations for map preloading
+  * altosui: Add launch-sites.txt
+  * altosui: Add a bunch more site locations
+  * altosui: Mark preload site location with red circles (like launch)
+  * Fix NCR Pawnee location
+  * Add HARA Bragg Farms site
+  * altosui: Change continutity colors to yellow/magenta
+  * altos: Add ability to read new TELEM files to ao_flight_test
+  * altos: Average height values for landing detection
+  * altos: Reduce height averaging filter time constant
+  * altos: Require sequencing through 'main' state before landing
+  * altosui: Standard text field in flight UI needs more width (now 20)
+  * altosui: Parse accel cal from 'c s' command
+  * altosui: Simple timeouts don't work with query data
+  * altosui: Add idle monitor dialog
+  * altos/altosui: Add pad orientation configure option
+
+  [ Bdale Garbee ]
+  * update version for a Bdale build for use turning on TeleMini v1.0
+  * simplify version in last changelog entry so git-dch is less confused
+  * update changelogs for Debian build
+  * update changelogs for Debian build
+  * stop doing automatic tag push during builds
+  * build depend on bluetooth dev package
+  * clean up all existing lintian warnings
+  * rewind changelog for re-build of 0.9.4.5
+
+ -- Bdale Garbee <bdale@gag.com>  Mon, 08 Aug 2011 09:37:04 -0600
+
+altos (0.9.1) unstable; urgency=low
+
+  [ Keith Packard ]
+  * ao-load: fix usage message to note that '=' is required for options
+  * altos/test: Add dependencies in the Makefile for ao_flight_test
+  * altos/test: auto-configure acceleration parameters from the log file
+
+  [ Anthony Towns ]
+  * ao_radio: generalise setup of packet size
+  * src/ao_gps_skytraq: simplify parsing code
+  * src/ao_gps_skytraq.c: Update logging rate to 10Hz
+
+  [ Bdale Garbee ]
+  * tie bringup scripts to Bdale's bench TeleDongle
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 01 Apr 2011 20:02:12 -0600
+
+altos (0.9) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * prepare to release
+  * update changelogs for Debian build
+  * update turnon script to prefer TeleDongle as programmer
+
+  [ Keith Packard ]
+  * altos: Program default flight log max value for new boards
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 19 Jan 2011 12:46:47 -0700
+
+altos (0.8.1+81+g4ae724f) unstable; urgency=low
+
+  * we need an install target to prevent parent dir make from failing
+
+ -- Bdale Garbee <bdale@gag.com>  Tue, 18 Jan 2011 23:55:36 -0700
+
+altos (0.8.1+79+g9a5666f) unstable; urgency=low
+
+  [ Keith Packard ]
+  * doc: Don't delete telemetrum-outline.pdf
+  * doc: Add v0.9 features from altosui to documentation.
+
+  [ Bdale Garbee ]
+  * add 0.9 revision entry, with caveat about telemetry format change
+  * freshen copyright year
+
+  [ Keith Packard ]
+  * fat: Add firmware for v1.1 and docs to mac/windows/linux installers
+  * fat: Add docs to Linux package
+  * doc: Build with 'make all' from top level. Build with 'make fat'
+
+ -- Bdale Garbee <bdale@gag.com>  Tue, 18 Jan 2011 23:47:52 -0700
+
+altos (0.8.1+71+gda42f40) unstable; urgency=low
+
+  [ Keith Packard ]
+  * doc: Add telemetrum mounting template in svg and pdf forms
+
+  [ Mike Beattie ]
+  * Re-order and re-arrange eeprom download dialog
+  * Convert EepromSelect dialog to use a GridBag
+  * Close serial port if Download/Delete dialog is cancelled.
+
+  [ Keith Packard ]
+  * altosui: Reset eeprom download instance variables before reading
+    flight
+  * altosui: Make serial debug more complete and accurate
+  * altosui: Use long input flush timeout when remote.
+  * altosui: Remove debug message when eeprom downloads are complete.
+  * altosui: Ensure serial device is closed after eeprom download
+    finishes
+  * altosui: Require 4 sats to light up the 'GPS locked' light.
+  * doc: inkscape tracks the filename inside the document
+  * altos: TELEMETRY PROTOCOL CHANGE. Switch to 16-bit serial numbers.
+  * altos: Flush log when full
+
+  [ Anthony Towns ]
+  * altos: Restructure skytraq NMEA parsing code to save some space
+
+  [ Keith Packard ]
+  * altos: average 512 accel/baro samples at startup instead of 1000
 
-  * build for Debian from git
+  [ Mike Beattie ]
+  * altos: Added check for an accel value above 1.5g
+  * altos: Added check for out of bounds accel
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 20:48:23 -0600
+  [ Keith Packard ]
+  * altos: Add DATA_TO_XDATA to linux test harness
+  * altosui: Remove spurious colons from eeprom selection headers
 
-altos (0.4+135+g82d24bc) unstable; urgency=low
+  [ Mike Beattie ]
+  * Rework invalid accel cal detection code
 
-  * build for Debian from git
+  [ Keith Packard ]
+  * altos: Auto-calibrate linux-based flight testing code
+  * altos: Ensure flight code gets first crack at new ADC data
+  * altos: Sample the accelerometer reference voltage on v1.1 boards
+  * altos: Optimize fetching of ADC data in flight code
+  * altos: Use 5V reference data to correct accelerometer measurements.
+  * doc: Remove mention of ao_wake_task
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 20:17:31 -0600
+  [ Bdale Garbee ]
+  * update documentation to reflect reality that modifying a board for
+    separate pyro battery is not trivial
 
-altos (0.4+133+g2fda90c) unstable; urgency=low
+ -- Bdale Garbee <bdale@gag.com>  Tue, 18 Jan 2011 17:26:33 -0700
 
-  * build for Debian from git
+altos (0.8.1+42+g646e192) unstable; urgency=low
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 20:12:44 -0600
+  [ Keith Packard ]
+  * altosui: Show dialog after successful delete or when no flights
+  * altosui: Ensure serial line is flushed after disabling remote link
+  * altosui: Add preference for serial debugging.
 
-altos (0.4+132+ga40f45b) unstable; urgency=low
+ -- Bdale Garbee <bdale@gag.com>  Fri, 14 Jan 2011 18:00:51 -0700
 
-  * build for Debian from git
+altos (0.8.1+37+g97b1ca9) unstable; urgency=low
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 20:12:22 -0600
+  [ Keith Packard ]
+  * altos: Add configuration parameter for maximum flight log size
+  * altos: support storage of multiple flights.
+  * altos: Speed up at45 and 25lc erase speeds
+  * altos: white space fix
+  * altos: report flight log offsets in hex block numbers instead of
+    bytes
+  * altos: ensure erase mark is written when erasing flights
 
-altos (0.4+130+gc7f7a1e) unstable; urgency=low
+  [ Anthony Towns ]
+  * altos: Optimize Morse code generation
 
-  * build for Debian from git
+  [ Keith Packard ]
+  * altos: Check requested log max size against available space
+  * altos: Check for full log and complain
+  * altosui: Add configuration of flight log size
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 18:57:56 -0600
+  [ Anthony Towns ]
+  * altos: Remove unused accel_vel_mach and accel_vel_boost variables
+  * altos: Remove redundant initialization of ao_interval variables
+
+  [ Keith Packard ]
+  * altosui: Split eeprom download code apart
+  * altos: oops -- 'e' command was only showing 7 of the 8 bytes per
+    line
+  * altosui: Add support for parsing list of flights from the 'l'
+    command
+  * altosui: Add eeprom 'manage' ui to download and delete multiple
+    flights
+  * altos: check for valid flight number in ao_log_delete
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 14 Jan 2011 14:30:37 -0700
+
+altos (0.8.1+18+g408a3e0) unstable; urgency=low
+
+  [ Anthony Towns ]
+  * altosui: move maps to subdir, fix E/W mismatch
+
+  [ Keith Packard ]
+  * altos: clean up radio abort paths. Share radio code.
+  * altos: eliminate ao_wake_task
+  * windows: Add compatibility IDs to telemetrum.inf
+  * windows: Update NSIS installer file to use compatibility IDs
+  * altos: Split out SPI driver.
+  * altos: Add telemetrum-v1.1 directory
+  * altos: packet and usb i/o routines use 'char', not 'uint8_t'
+  * altos: Simplify storage API
+  * altos: Require manual flight erasing.
+  * altos: Move common storage code to ao_storage.c. Add M25P80 driver
+  * altos: Rip out 'optimization' in ao_log_scan
+  * altos: Mark end of available flight list with 'done' to make the UI
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 07 Jan 2011 00:51:41 -0700
+
+altos (0.8.1+4+gea95c06) unstable; urgency=low
+
+  * fix symlink paths in rules file
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 01 Dec 2010 00:14:24 -0700
+
+altos (0.8.1+2+gea7130e) unstable; urgency=low
+
+  * modify Debian package build to deliver one copy of background.png
+    for
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 01 Dec 2010 00:03:14 -0700
+
+altos (0.8.1) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * moved doc dir in web content to AltOS tree
+  * fix publish target in doc/Makefile
+  * fix section layering
+  * fix an Altos vs Altus typo in the docs
+  * adding Bdale's release process document to the source tree
+
+  [ Keith Packard ]
+  * windows: Add jfreechart.jar and jcommon.jar to windows install image
+  * Move "Releasing" to top level
+  * Add minimal release testing plan to Releasing
+
+  [ Bdale Garbee ]
+  * releasing 0.8.1
+
+ -- Bdale Garbee <bdale@gag.com>  Mon, 29 Nov 2010 21:46:54 -0700
+
+altos (0.8) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altosui: Correct windows hardware IDs for nsis installer file
+  * windows: try harder to get windows install to work
+  * windows: remove some non-existent .inf file section references
+  * windows: more .inf file hacking
+  * windows: Bump .inf file version
+
+  [ Bdale Garbee ]
+  * declaring 0.8 released
+  * update changelogs for Debian build
+  * update changelogs for Debian build
+  * rewind changelog to recover from stupid build failure
+
+ -- Bdale Garbee <bdale@gag.com>  Sun, 28 Nov 2010 18:32:12 -0700
+
+altos (0.7.1+168+gcb08bc2) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altosui: Split out flight monitoring to separate window
+  * altosui: Create buttons for main actions
+  * altosui: Fix channel setting at serial open time
+  * altosui: Fix more calls to AltosPreferences.channel()
+  * altosui: Create abstract interface for flight data display
+  * altosui: Add tab UI with 'pad' mode.
+  * altosui: Add ascent, descent and landed tabs
+
+  [ Bob Finch ]
+  * Updated PKGBUILD-git.altos
+
+  [ Keith Packard ]
+  * altos: Use grey leds when unlit - easier to see
+
+  [ Anthony Towns ]
+  * use value_font for values
+  * add --replay command line argument
+  * read preferences for --replay
+  * reduce font size for FlightInfoTable
+  * use grayled.png for off
+  * add compass bearing to voice output
+  * add compass bearing during descent
+  * add site map tab, at least for QRS launches
+  * tabs -> spaces
+  * make infotable scrollable, revert its fontsize to 14
+  * pull up maps for arbitrary locations
+  * better error behaviour if no map
+
+  [ Bdale Garbee ]
+  * first cut at instructions on how to re-flash TM and TD devices based
+    on email
+  * fold in content from Keith's email on the re-flashing subject
+
+  [ Keith Packard ]
+  * altosui: Add RF calibration to TeleMetrum config dialog
+
+  [ Anthony Towns ]
+  * AltosTelemetryReader: actually open serial port
+  * AltosSiteMap: add targeting circles around landing site
+
+  [ Bdale Garbee ]
+  * continuing to work on the docs
+
+  [ Keith Packard ]
+  * altosui: Eliminate unncessary import altosui lines
+  * altosui: Open serial device at 'new' time. Prohibit duplicate opens.
+  * altosui: Replace flight status table with labels, fix resize.
+  * altosui: With --replay option, exit when replay window is closed
+  * altosui: oops, missed a file in the previous commit
+  * altosui: eliminate menu bar, moving elements to buttons.
+  * altosui: add reboot button to telemetrum configuration UI
+
+  [ Bdale Garbee ]
+  * add a paragraph about forcing TM back to idle mode if an accel cal
+    goes badly
+
+  [ Keith Packard ]
+  * altosui: Add callsign configuration in AltosUI configuration dialog
+  * altosui: Cleanup flight UI layout
+  * altosui: Add igniter status to ascent and descent tabs
+
+  [ Anthony Towns ]
+  * altosui: tile site maps
+  * AltosSiteMapTile: autoscale to about 2 nmi per tile
+  * AltosSiteMapTile: adjust centering calculation
+  * AltosSiteMapTile: adjust scale to 1 nmi per tile
+
+  [ Keith Packard ]
+  * altosui: Add igniter ground testing code
+  * altosui: Unify datafile selection to AltosDataChooser
+  * altosui: Clean up global AltosUI configuration settings dialog
+  * doc: Add preliminary altosui documentation
+  * doc: git ignore generated doc files
+  * doc: Document the 'Flash Image' operation.
+  * altosui: Use timeouts to recover from broken packet links.
+
+  [ Bdale Garbee ]
+  * update turnon scripts to use stashed copies of stable release
+    firmware
+
+  [ Keith Packard ]
+  * altosui: switch channel selector to combo box. Shorten displayed
+    device names
+  * altosui: When switching log files, don't terminate log thread
+  * altosui: calling thread.interrupt with null thread doesn't work well
+  * altosui: Initialize display thread state in constructor instead of
+    run
+  * altosui: Fix channel changing in flight UI to actually work
+
+  [ Anthony Towns ]
+  * AltosSiteMap: add autoscroll and grabndrag scroll
+  * AltosSiteMapTile: seperate map and drawing layers
+  * AltosSiteMap: automatic fetching of map data
+  * AltosSiteMapTile: draw boost circle as well as landed
+  * AltosAscent/Descent: tidy up layout
+  * AltosDescent: switch elev from height to range
+
+  [ Keith Packard ]
+  * altosui: re-indent
+
+  [ Anthony Towns ]
+  * AltosSiteMap: major refactoring
+  * Add GrabNDrag.java
+
+  [ Keith Packard ]
+  * altosui: change descent tab formatting to four columns
+
+  [ Anthony Towns ]
+  * AltosSiteMap: be more polite about preferred size
+  * altosui: reindent
+
+  [ Keith Packard ]
+  * altosui: Rewrite info table to mix with scroll pane well. Fix
+    startup size
+
+  [ Anthony Towns ]
+  * AltosSiteMap: better gps check, lower zoom
+
+  [ Keith Packard ]
+  * altosui: Set site map flight path lines to 6 pixels anti-aliased.
+
+  [ Anthony Towns ]
+  * AltosSiteMap: explain tile size better
+  * AltosSiteMap: extend map if rocket goes far away
+  * AltosSiteMap: try to get new tile construction right
+  * AltosSiteMap: thread safe tile addition
+  * AltosSiteMap: refactor tile collection
+  * AltosSiteMap: never accept 0,0 as lat/long
+  * AltosSiteMap: limit nr of tiles to 200x200
+
+  [ Keith Packard ]
+  * altosui: When fixing eeprom gps time information, make GPS data
+    valid
+
+  [ Anthony Towns ]
+  * AltosSiteMap: ensure buffer around active tile
+  * altosui: improve sitemap scrolling behaviour
+  * altosui: keep sitemap more centred on rocket
+
+  [ Keith Packard ]
+  * altos: Add on/off modes to 'C' command
+
+  [ Bob Finch ]
+  * Added PKGBUILDs for deps into contribs
+
+  [ Keith Packard ]
+  * altosui: Close serial port when debug link fails
+  * altos: assume igniter worked.
+
+  [ Bdale Garbee ]
+  * add a rudimentary --help for command line use
+
+  [ Keith Packard ]
+  * doc: Add internal documentation for AltOS
+
+  [ Anthony Towns ]
+  * altosui: don't switch away from user selected tab
+  * altosui: sitemap uses rocket gps if no pad gps
+
+  [ Bdale Garbee ]
+  * merge Keith's AltosUI documention into "the big book"
+  * lose the placeholder on how GPS works, as it's going to be a
 
-altos (0.4+129+gebaf8b0) unstable; urgency=low
+  [ Keith Packard ]
+  * altos: remove unused variable from ao_igniter
+  * altos: Don't abort radio transmissions with ao_radio_abort
+  * altos: Make radio test command careful with the radio mutex.
+  * altosui: Make AltosSerial.flush_input keep reading while non-empty
+  * altosui: New AltosSerial.set_radio function sets channel/call
+  * altosui: Disable radio configation over packet link.
+  * altosui: Let people fire igniters that don't read as 'ready'
+  * altosui: Make sure packet mode is turned off when the connection
+    fails
+
+  [ Anthony Towns ]
+  * docs: Document altosui "Graph Data" button
 
-  * build for Debian from git
+  [ Keith Packard ]
+  * Move altosui to the top level, placing libaltos inside it.
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 18:56:20 -0600
+  [ Anthony Towns ]
+  * doc: Document altosui "Site Map" tab
 
-altos (0.4+120+g5969d1d) unstable; urgency=low
+  [ Keith Packard ]
+  * Missing change to top level Makefile to build altosui
 
-  * build for Debian from git
+  [ Bdale Garbee ]
+  * fix missing section close in Site Map content
+  * tweak rev history
+  * manually fold in documentation work from the master branch
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 18:24:50 -0600
+  [ Keith Packard ]
+  * doc: Reformat altos to use sections for each function
+  * doc: Add more authors, fix URL formatting, note that AltosUI
+    actually exists
+  * doc: Rename telemetrum-doc as altusmetrum
 
-altos (0.4+118+gd861218) unstable; urgency=low
+  [ Bdale Garbee ]
 
-  * build for Debian from git
+ -- Bdale Garbee <bdale@gag.com>  Thu, 25 Nov 2010 01:10:18 -0700
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 18:14:02 -0600
+altos (0.7.1+36+g811ced6) unstable; urgency=low
 
-altos (0.4+116+g7fcbe76) unstable; urgency=low
+  [ Bdale Garbee ]
+  * remove the csv build dep, as that code will be abandoned
 
-  * build for Debian from git
+  [ Keith Packard ]
+  * altosui: Remove ability to graph data in .csv files
+  * altosui: Separate out jfreechart and jcommon directories
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 18:10:06 -0600
+ -- Bdale Garbee <bdale@gag.com>  Wed, 06 Oct 2010 17:47:32 -0600
 
-altos (0.4+110+g7aa2519) unstable; urgency=low
+altos (0.7.1+32+g8103432) unstable; urgency=low
 
-  * build for Debian from git
+  * add build-dep on libcsv-java
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 14:40:58 -0600
+ -- Bdale Garbee <bdale@gag.com>  Wed, 06 Oct 2010 16:25:57 -0600
 
-altos (0.4+106+g1491515) unstable; urgency=low
+altos (0.7.1+28+gd8a2f4c) unstable; urgency=low
 
-  * build for Debian from git
+  [ Bdale Garbee ]
+  * make the column headers comma separated, too, so they align with the
+    data
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 14:03:48 -0600
+  [ Keith Packard ]
+  * altosui: set default .csv file name in file save dialog
+  * altosui: Remember directory containing firmware files
 
-altos (0.4+105+g0fcc426) unstable; urgency=low
+  [ Anthony Towns ]
+  * Add graphing.
+  * Add JFreeChart to Makefile.am
 
-  * build for Debian from git
+  [ Keith Packard ]
+  * altosui: Use recorded ground acceleration when reading eeprom files
+  * altosui: Write raw sensor data to .csv files
+  * altosui: Factor some UI elements into separate classes
+  * altosui: remove unused ReplayThread wrapper classes
+  * altosui: Require 4 sats for 'good' GPS data
+  * altosui: Create iterables for log file scanning. Split out display
+    threads
+  * altosui: .CSV output: add link quality, gps hdop and sat C/N0
+    numbers
+  * altosui: Deal with eeprom dates going backwards across wrap
+
+  [ Anthony Towns ]
+  * Hax0r graphing to support telem/eeprom files
+
+  [ Keith Packard ]
+  * altosui: use Altos constants in graphing code
+
+  [ Anthony Towns ]
+  * Add callsign/serial/flight to graph title
+
+  [ Keith Packard ]
+  * altosui: Add KML file export.
+
+  [ Bdale Garbee ]
+  * add build dep on jfreechart lib
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 06 Oct 2010 16:19:12 -0600
+
+altos (0.7.1) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Add firmware and libaltos to 'fat' target at top-level
+  * altosui: Remove some debug printfs.
+  * altosui: libaltos.so is built with libtool -- it's in
+    ../libaltos/.libs
+  * Add --with-fat-dir configure option to publish finished stand-alone
+    bits
+  * altosui: Fix telemetry file reader to handle tick count wrapping
+  * altosui: Remove debug printfs from AltosTelemetryReader
+  * altosui: Fill in time value of last Eeprom record read from file
+  * altosui: Stop parsing eeprom file after hitting 'landed' state
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 10 Sep 2010 00:09:02 -0600
+
+altos (0.7+96+g48f5799) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altosui: conflating USB product and vendor IDs is a bad idea
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 09 Sep 2010 21:29:13 -0600
+
+altos (0.7+94+g1ac3d7e) unstable; urgency=low
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 14:03:41 -0600
+  * initial cut at an altosui man page
 
-altos (0.4+102+g645cc90) unstable; urgency=low
+ -- Bdale Garbee <bdale@gag.com>  Thu, 09 Sep 2010 20:34:38 -0600
 
-  * build for Debian from git
+altos (0.7+92+g0ea7576) unstable; urgency=low
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 12:59:36 -0600
+  [ Bdale Garbee ]
+  * handle versioning of ihx files (poorly) by just wildcarding the file
+    name
 
-altos (0.4+101+gf54e075) unstable; urgency=low
+  [ Keith Packard ]
+  * altosui: Need to have JVM include path substituted into libaltos
+    Makefile
+  * altosui: Store libaltos.so in $(libdir)/altos
+  * altosui: Display error dialog when AltOS JNI library can't be loaded
 
-  * build for Debian from git
+ -- Bdale Garbee <bdale@gag.com>  Thu, 09 Sep 2010 20:05:44 -0600
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 12:59:28 -0600
+altos (0.7+86+g6c0ae7e) unstable; urgency=low
 
-altos (0.4+99+g022e77d) unstable; urgency=low
+  [ Keith Packard ]
+  * altosui: Add explicit requirement to create classes directory
+  * altosui: fix telemetrum.inf FFFE:000A product names on AMD64 and
+    ia64
+  * altosui: hack to make JAVAROOT directory get created before javac
+    runs
+  * altosui: remove FATJAR from all-local to avoid building fat .jar
+    file
+
+  [ Bdale Garbee ]
+  * add libtool to build deps
+
+  [ Keith Packard ]
+  * altosui: Add windows installer build using 'nsis'
+  * altosui: ignore built files
+  * altosui: oops. renamed the nsis file to altos-windows.nsi
+  * icon: add some icons for application programs
+  * altosui: Add icons to application and Windows menus.
+  * Use autotools for altosui and libaltos
+  * altos: add some SDCDB config files
+  * Ignore libtool files.
+  * Add version numbers to released files. Set version to 0.7.1
+  * Add top-level 'fat' target to aid building distribution files
+  * altosui: Fix windows installer to ship correct files
+  * altosui: Fix up Mac OSX .zip file
+  * altosui: Fix linux fat distribution
+  * altosui: Return AO_LOG_INVALID instead of exception for eeprom files
+  * altosui: Remove debugging printf from AltosEepromReader
+  * altosui: Eeprom files contain only one date; save it.
+  * altosui: Add elevation and range data to main display
+  * altosui: When replay thread is interrupted, don't make final report
+  * altosui: Start idle thread after the rocket leaves the pad
+  * altosui: Add AltosVoice.drain() to wait for queued speech to finish
+  * altosui: Prevent voice altitude data from queueing up
+  * altosui: Use local .class files in the classpath
+  * altosui: Record flight number when scanning file, not when running
+  * altosui: eeprom files place 'boost' time in the flight number
+    record.
+  * altosui: ensure that 'altosui' script is installed. Pass arguments
+    along.
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 09 Sep 2010 15:43:28 -0600
+
+altos (0.7+53+g59798c6) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altosui: Abort flashing if debug port isn't working
+  * altosui: allow flashing to be canceled from the rom config dialog
+  * altosui: Hide internal rom config UI helper function
+  * altosui: Remove some debug printfs from AltosRomconfig class
+  * altosui: Post error dialog on invalid ROM config values.
+  * altosui: build Mac OS .zip file to include paths
+  * altosui: Report telemetry CRC errors in UI
+  * altosui: Deal with altos bug setting radio channel while monitoring
+  * altosui: Allow 'connect to device' when already connected
+  * Revert "altosui: Deal with altos bug setting radio channel while
+    monitoring"
+  * altosui: Must flush serial line after configuring for telemetry
+  * altosui: Catch I/O errors on telemetry device, report to user
+
+ -- Bdale Garbee <bdale@gag.com>  Sat, 04 Sep 2010 00:46:12 -0400
+
+altos (0.7+40+g59a40f6) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * add distclean targets to libaltos and altosui to all Debian package
+    to build, and clean up other distclean content as needed
+
+  [ Keith Packard ]
+  * altosui: missed AltosReader.class in the Makefile
+  * altos: Bounds check Skytraq GPS tracking data array
+  * altosui: Remove Manifest.txt from git repo as it's built now
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 02 Sep 2010 00:44:15 -0400
+
+altos (0.7+28+gd006c5e) unstable; urgency=low
+
+  * add runtime dependencies for altos binary package
+
+ -- Bdale Garbee <bdale@gag.com>  Tue, 31 Aug 2010 00:20:00 -0400
+
+altos (0.7+26+gc35632e) unstable; urgency=low
+
+  * don't build all the "fat" jar deliverables by default
+
+ -- Bdale Garbee <bdale@gag.com>  Mon, 30 Aug 2010 19:37:40 -0600
+
+altos (0.7+23+g25764fc) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * fix up for an 0.7 release
+  * update changelogs for Debian build
+
+  [ Keith Packard ]
+  * libaltos: AltusMetrum devices use more than one USB ID.
+  * altosui: provide separate flush_input/flush_output for serial. deal
+    with monitor automatically
+  * altosui: discard invalid lines while reading Eeprom flight data
+  * libaltos: Mac OS X cannot use 'poll(2)' on serial lines.
+  * libaltos: Missing OS_LDFLAGS on cjnitest build
+  * libaltos: cjnitest needs altos_flush now
+  * altos: flush pending output when terminating packet mode
+  * altos: Abort radio harder when terminating packet mode.
+  * altos: shut down packet mode cleanly
+  * libaltos: Fix windows build.
+  * libaltos: Improve Makefile
+  * Update telemetrum.inf to include all current USB ids.
+  * libaltos: Add pre-built Mac OS X libaltos.dylib
+  * libaltos: Add pre-built Windows .dll
+  * altos: Windows sends USB Out packets of 0 length. Ack them.
+  * libaltos: Use overlapped I/O on windows
+  * altosui: Build linux, mac and windows archives on Linux
+  * altosui: build debian-style altosui too
+  * altosui: Devices with USB id 0x000a always get listed
+
+  [ Bdale Garbee ]
+  * continue even if rm's don't have anything to do
+  * make invocation of 'install' pathless to work on more Unix variants
+  * add a .gitattributes file, configuring the Mac and Windows binary
+    library
+
+ -- Bdale Garbee <bdale@gag.com>  Mon, 30 Aug 2010 19:07:13 -0600
+
+altos (0.7) unstable; urgency=low
+
+  * update changelogs for Debian build
+  * fix up for an 0.7 release
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 27 Aug 2010 22:25:38 -0600
+
+altos (0.6+375+g0bd4cc0) unstable; urgency=low
+
+  * fix path to installed shared library
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 27 Aug 2010 13:13:14 -0600
+
+altos (0.6+373+gcf65c6b) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altosui: rename AltosEeprom -> AltosEepromDownload, split out Altos
+    constants
+  * altosui: Capture config and version info in .eeprom files
+  * altosui: Merge gps date and time classes into gps class
+  * altosui: Clear displayed data rows as needed.
+  * altosui: Split status and info panels into separate files
+  * altosui: Explicitly initialize Altos class
+  * altosui: Split flight record out of telemetry class
+  * altosui: Compute flight state from eeprom data
+  * altosui: Add comments to Eeprom reader
+  * altos: add callsign to packet mode, increase payload to 64 bytes
+  * altosui: Start adding code to write csv files from eeprom/telem
+    files
+  * ao-dumplog: add --channel option (for use with -R option)
+  * libaltos: integrate Windows support.
+  * altosui: Select devices by USB vendor/product ID.
+  * altos: Define USB product ID in per-product Makefile.defs file
+  * altosui: Make teledongle callsign configurable
+  * altosui: Add TeleMetrum configuration
+  * altosui: Set callsign when fetching eeprom data over the air
+
+  [ Bdale Garbee ]
+  * add freetts as a build dep
+  * working on java packaging details
+
+  [ Keith Packard ]
+  * altosui: Add .ihx file reading code and stub out flashing UI
+  * altosui: Add debug dongle API, split flash UI out
+  * ao-dumplog: Fix --remote and --channel options to actually work
+  * altosui: pad TM config dialog values to avoid clipping descenders
+  * libaltos: use pipe to wake up getchar on close. use mutexes
+  * altosui: Add lots more cc1111 debug interface functions
+  * altosui: remove debug printf from AltosHexfile
+  * altosui: flush serial output before waiting for reply
+  * altosui: Remove debug printf from AltosRomconfig
+  * altosui: Finish device programming code
+  * altos: Place rom config variables in fixed location
+  * altosui: make default Manifest look for built-in freetts
+  * altosui: Separate out log file choosing dialog to share with CSV
+    generator
+  * altosui: refactor logfile chooser dialog to share more code
+  * altosui: Add ability to create CSV file from telem or eeprom files
+  * altosui: disable radio monitoring while using serial line for
+    debugging
+  * altosui: Delay mapping Flash UI until flashing actually starts
+  * altosui: fetch existing romconfig for flashing
+  * altosui: always display romconfig ui while flashing
+  * altosui: write USB serial number string while flashing
+  * altosui: flush replies from serial link when entering debug mode
+  * altos: always rebuild ao_product.c to track git version
+  * altos: print GPS state flags in GPS 'g' command
+  * altos: mark gps date written only after it gets into eeprom
+  * altosui: Move number parsing code to Altos general class
+  * altosui: Add AltosGreatCircle constructors
+  * altosui: add rssi and distance/dir from pad to CSV files
+  * altosui: AltosEepromReader was mis-setting boost tick
+  * altosui: Add support for old (version < 3) telemetry files
+  * altosui: Serial line is in UTF-8 encoding. Deal with it.
+  * altosui: When parsing saved telem files, errors shouldn't abort file
+  * altosui: Remove debug printf from AltosState.java
+  * altosui: command line args are converted to csv format
+  * altos: prepare for sdcc 2.9.1
+
+  [ Bdale Garbee ]
+  * add a dummy install target
+  * lose the prebuild hook for now while I'm fumbling
+  * add install target for libaltos
+  * add an install target for altosui
+
+  [ Keith Packard ]
+  * altosui: add elevation and range information
+
+  [ Bdale Garbee ]
+  * fix up the wrapper's path to the jar file
+  * update Debian standards version
+  * fix permissions on installed jar file, switch from ao-view to
+    altosui in
+  * install altosui man page
+  * fix man page delivery path
+  * Revert "lose the prebuild hook for now while I'm fumbling"
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 27 Aug 2010 12:40:04 -0600
+
+altos (0.6+303+gb6da90b) unstable; urgency=low
+
+  * add freetts as a build dep
+  * move to science menu
+  * working on java packaging details
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 11 Aug 2010 22:11:28 -0400
+
+altos (0.6+292+g8fc261c) unstable; urgency=low
+
+  * see if my new freetts package works
+
+ -- Bdale Garbee <bdale@gag.com>  Sat, 31 Jul 2010 10:55:21 -0600
+
+altos (0.6+290+g7877496) unstable; urgency=low
+
+  [ Keith Packard ]
+  * altosui: check for closed serial device before reading
+  * altosui: Remove unnecessary freetts .jar files
+  * altosui: Close serial, join reader thread, free altos_file
+  * altosui: remove debug printf
+  * libaltos: build with java src encoding UTF8
+  * altosui: construct Darwin application directory
+  * Add Mac OS X packaging bits
+  * altosui: Switch eeprom extension back to .eeprom
+  * Moved Mac OS packaging to altosui dir
+  * altosui: remove option to install to alternate volume
+  * Make altosui test script executable
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 29 Jul 2010 13:30:19 -0600
+
+altos (0.6+277+gd184819) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * fix text since TM only has one led to blink
+  * add swig as a build dep
+  * add a jdk to the build deps
+
+  [ Keith Packard ]
+  * When the EP0 IN buffer is full, don't panic, just skip sending
+    another
+  * Add libaltos which talks to USB connected altos devices
+  * Switch AltosUI to libaltos for device access
+  * Present list of altos devices in nice format
+  * Re-enable freetts
+  * Re-enable Linux support for altosui.
+  * Darwin doesn't have strndup.
+  * libaltos needs -I. on all systems
+  * libaltos: make clean remove all built files
+  * libaltos: build fat 10.5-compatible library
+  * Add Mac OS X packaging files for altosui
+  * Add application icons for Mac OS X
+  * Clean up altosui build a bit
+  * Remove directories as .class file dependencies; it makes them get
+    rebuilt all the time
+  * Java clean ups -- use varargs where possible, remove
+    AltosSerialReader
+  * Java voice reporting cleanups.
+  * Force java source encoding to UTF-8
+  * Make ao_log_data re-entrant as it is used for both sensor and GPS
+    logs
+  * altosui: Catch errors opening USB devices. Limit list to relevant
+    devices
+  * Make voice and channel menus work.
+  * Add voice test command for help in adjusting volume.
+  * Remove debug printf
+  * altosui: Add eeprom data capture function. No UI yet.
+  * altosui: Add progress bar for eeprom downloading status
+  * altosui: Fix Save flight data monitor layout, add cancel
+  * altosui: Replace device dialog. Center eeprom monitor.
+  * altosui: report rocket ground bearing at landing only if known
+  * Reset GPS ready status when GPS comes unlocked on the pad
 
-  * build for Debian from git
+ -- Bdale Garbee <bdale@gag.com>  Thu, 29 Jul 2010 12:50:40 -0600
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 12:52:39 -0600
+altos (0.6+236+gcd8aa79) unstable; urgency=low
 
-altos (0.4+97+g90347b1) unstable; urgency=low
+  [ Keith Packard ]
+  * Telemetry code was mis-computing RSSI
+  * Switch DBG pins to GPIO when using any debug commands. Reboot to
+    restore.
 
-  * build for Debian from git
+  [ Bdale Garbee ]
+  * significant documentation update
+  * add build dep for sndfile
+  * reflect documentation file name change
+  * update to latest Debian standards version
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 12:39:41 -0600
+ -- Bdale Garbee <bdale@gag.com>  Tue, 20 Jul 2010 22:24:14 -0600
 
-altos (0.4+94+gf9126a2) unstable; urgency=low
+altos (0.6+224+g4766b13) unstable; urgency=low
 
-  * build for Debian from git
+  [ Bdale Garbee ]
+  * documentation updates from Bob
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 12:16:23 -0600
+  [ Mike Beattie ]
+  * Extension to KML output format, and minor bug fix
 
-altos (0.4+93+g4d7c4c6) unstable; urgency=low
+  [ Keith Packard ]
+  * ao-view: disable radio telemetry monitoring during channel change
+  * Add special code for USB panic's.
+  * ao-postflight: was walking off state.data array
+  * Abort any in-progress radio operation when changing radio channel
 
-  * build for Debian from git
+ -- Bdale Garbee <bdale@gag.com>  Thu, 24 Jun 2010 12:37:34 -0700
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 12:06:03 -0600
+altos (0.6+214+g16c4cae) unstable; urgency=low
 
-altos (0.4+91+g9a97abb) unstable; urgency=low
+  [ Bdale Garbee ]
+  * updates including a typo fix from Bob Finch to the PKGBUILD-git.altos
+  * merge a derivative of Bob Finch's mere mortals guide as getting started
+  * merge the altusmetrum-themes package
 
-  * build for Debian from git
+  [ Keith Packard ]
+  * Fix telemetrum.inf, tested by Adrian and Keithp
+  * Finish basic flight monitoring UI with voice using FreeTTS
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 11:58:29 -0600
+ -- Bdale Garbee <bdale@gag.com>  Tue, 18 May 2010 00:24:40 -0600
 
-altos (0.4+90+g24c337d) unstable; urgency=low
+altos (0.6+204+g6bd8513) unstable; urgency=low
 
-  * build for Debian from git
+  * lose the quotes since they apparently aren't necessary
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 11:58:18 -0600
+ -- Bdale Garbee <bdale@gag.com>  Wed, 12 May 2010 19:53:58 -0600
 
-altos (0.4+88+gf65fa2b) unstable; urgency=low
+altos (0.6+202+gae6854d) unstable; urgency=low
 
-  * build for Debian from git
+  * update desktop file for conformance with current standards
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 11:57:52 -0600
+ -- Bdale Garbee <bdale@gag.com>  Wed, 12 May 2010 17:04:31 -0600
 
-altos (0.4+86+gcf1fa8b) unstable; urgency=low
+altos (0.6+200+g68eaaa6) unstable; urgency=low
 
-  * build for Debian from git
+  * add initial package build scripts for Arch Linux from Bob Finch
+  * add desktop file provided by Bob Finch
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 12 May 2010 16:44:27 -0600
+
+altos (0.6+195+gcde60f1) unstable; urgency=low
+
+  * Revert "Merge remote branch 'remotes/origin/fix-reset'"
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 06 May 2010 13:59:16 -0600
+
+altos (0.6+193+g314d27a) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Don't change dbg clock while changing reset_n. Use 20ms everywhere
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 06 May 2010 12:47:51 -0600
+
+altos (0.6+190+g8b6767e) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Fix all stdio reading functions to be __critical
+  * Use ao_delay to sleep for 2 seconds instead of trying ao_sleep
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 05 May 2010 02:34:06 -0600
+
+altos (0.6+187+g8702f49) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * more text created during SFO->DEN flight
+  * initial attempt at a telemetrum turn on script .. needs work
+
+  [ Keith Packard ]
+  * Revert "Add optional 's' command to packet slave to enable/disable
+    slave mode"
+  * Disable interrupts while reading from stdin
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 05 May 2010 01:52:27 -0600
+
+altos (0.6+180+g99094f0) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Increase reset switch time to 100ms
+  * Add optional 's' command to packet slave to enable/disable slave
+    mode
+  * More ALtosUI changes
+  * Autodetect flite voice registration function
+
+  [ Bdale Garbee ]
+  * capture work done on SFO->DEN flight
+  * add some RF usage information from an email reply sent today, and
+    re-indent
+
+ -- Bdale Garbee <bdale@gag.com>  Tue, 27 Apr 2010 00:17:57 -0600
+
+altos (0.6+171+g9394393) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * wrong Yaesu model
+
+  [ Keith Packard ]
+  * Only have the slave return a packet if it received one.
+
+ -- Bdale Garbee <bdale@gag.com>  Sat, 10 Apr 2010 15:01:04 -0600
+
+altos (0.6+168+gc0ee1ae) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * INSTALL file changed by auto tools
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 11:54:11 -0600
+  [ Keith Packard ]
+  * libflite may forget to reference libasound
 
-altos (0) unstable; urgency=low
+ -- Bdale Garbee <bdale@gag.com>  Fri, 09 Apr 2010 00:08:24 -0600
+
+altos (0.6+164+g5c3b6e2) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Start adding java-based UI
+  * Add telemetry data parsing code
+  * Add Windows install .inf file
+  * Fix windows install file
+  * Add telem parsing code
+  * Steal C code from ao-view
+  * Display table of flight info. gps is not working yet though
+  * Fix up table formatting
+  * Fix status update
+  * Fix state updates
+  * Clean up GPS data formatting
+  * Report current gps nsat, not last locked nsat
+  * Remove GPS data missing from skytraq. Save max height/accel/speed
+  * Remove unused cell renderer class
+  * Make .jar file
+  * Use RXTX for serial comm. Add logdir preference saving
+  * Make ao_radio_idle keep trying to get the radio to idle.
+  * Don't abort the radio when enabling telemetry monitoring
+  * Clean up some altosui comments
+  * Switch TeleMetrum from v0.2 to v1.0
+  * Add Linux device discovery
+  * Interrupt running replay thread when starting another replay
+  * serial port read function cannot be interrupted. poll every 1 second
+  * TD reports "not-connected" when GPS has 0 sats
+  * Tasks may move in task structure as a result of ao_exit
+  * Enable telemetry monitoring
+  * Use 16-bit flite voice (which appears to have changed symbols
+    recently)
+  * When changing RESET line, delay 20ms
+
+  [ Bdale Garbee ]
+  * choose a better set of docbook xsl files
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 08 Apr 2010 19:56:27 -0600
+
+altos (0.6+131+g6629ec5) unstable; urgency=low
+
+  * lose the url entirely for now
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 08 Apr 2010 12:43:01 -0600
+
+altos (0.6+129+g934434f) unstable; urgency=low
+
+  * fix typo in url
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 08 Apr 2010 12:41:38 -0600
+
+altos (0.6+127+g05ad583) unstable; urgency=low
+
+  * need another build dep
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 08 Apr 2010 12:35:04 -0600
+
+altos (0.6+125+g8f1d47e) unstable; urgency=low
+
+  * make lintian happy
+  * tweak copyright assertion
+  * crudely incorporate "day in the life" info from web page
+  * rewrite urls in docbook format
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 08 Apr 2010 12:28:04 -0600
+
+altos (0.6+119+g00f49c8) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * update changelogs for Debian build
+  * move gbp.conf into debian/
+  * initial harness for documentation
+  * fix typo
+
+  [ Keith Packard ]
+  * Round radio calibration value instead of truncating
+  * Document the ao-dumplog '--remote' flag for radio-link downloads
+
+ -- Bdale Garbee <bdale@gag.com>  Tue, 30 Mar 2010 23:19:15 -0600
+
+altos (0.6+108+g6e61170) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Need to duplicate new altitude conversion code in aoview.
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 04 Mar 2010 17:33:27 -0700
+
+altos (0.6+106+ga1478f6) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Add ao_radio_xmit to help test boards without flashing them.
+  * Allow product names to have suffixes (like board revisions)
+  * Fix and document the ao-rawload --run flag
+  * Add LED test
+  * Leave .ihx files in the build directory too - easier to debug that way
+  * Eliminate deadlock when writing config from radio link
+  * Fix up LED colors for each product.
+  * Add .gitignore for ao-bringup
+
+  [ Bdale Garbee ]
+  * create a turn on script for lighting up TeleDongle v0.2 boards
+
+ -- Bdale Garbee <bdale@gag.com>  Sat, 27 Feb 2010 17:35:32 -0700
+
+altos (0.6+95+g2f45953) unstable; urgency=low
+
+  * update changelogs for Debian build
+  * conditionalize use of git on executability of /usr/bin/git binary
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 24 Feb 2010 17:30:00 -0700
+
+altos (0.6+88+gdeccc10) unstable; urgency=low
+
+  * add gawk as a build dependency since strtonum is a gawk extension
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 24 Feb 2010 16:44:35 -0700
+
+altos (0.6+86+g2491282) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Disable monitor mode before attempting radio test.
+  * Ensure that ao_alarm waits at least the specified time
+  * Remove dbg driver code from telemetrum/teledongle
+  * Add AT45DBxx1D driver
+  * Remove green LED and temp sensor from v0.2 code
+  * Add at45db161d.h header file for new flash part.
+  * Add 'f' command to display flash status register contents
+  * Dump more flash parameters for the flash_status command
+  * Dump config block from read/write config and flash_status commands
+  * Don't set ao_flash_setup_done until we're actually done.
+  * Remove flash debugging printfs
+  * Add simple gps dump command 'g'
+  * Force idle mode by shorting the SPI clock to ground at boot time.
+  * Switch to using internal cc1111 temperature sensor
+  * Missing ao_mutex_put in gps_dump
+  * Compute daytime using GPS as time base
+  * Pull in a bit more data for filtering the start of the boost
+  * Log GPS data on pad after boost detect.
+  * Change barometer conversion code to shrink conversion table
+  * Change altos build process to support per-product compile-time
+    changes
+  * Update .gitignore files
+  * Quiet make output.
+  * Add back stack size checking to altos linking phase
+  * Must install .map files for ao-load to work
+
+ -- Bdale Garbee <bdale@gag.com>  Wed, 24 Feb 2010 14:53:01 -0700
+
+altos (0.6+60+g10d1bbc) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Use ao_radio_get/ao_radio_put in packet code.
+
+ -- Bdale Garbee <bdale@gag.com>  Sat, 19 Dec 2009 13:53:32 -0700
+
+altos (0.6+58+gbbb152c) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Re-order config values. Change frequency to cal
+  * Allow radio calibration to be set from ao-load
+  * Add --cal to man page
+
+ -- Bdale Garbee <bdale@gag.com>  Sat, 19 Dec 2009 12:05:26 -0700
+
+altos (0.6+53+gcd49847) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Remove send_serial and serial_baud commands.
+  * Make ao_cmd_decimal produce both 32 and 16 bit values.
+  * Add radio calibration configuration.
+
+ -- Bdale Garbee <bdale@gag.com>  Sat, 05 Dec 2009 11:02:49 -0700
+
+altos (0.6+49+g9a1d7dd) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Change default callsign to "N0CALL"
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 04 Dec 2009 21:01:55 -0700
+
+altos (0.6+47+g4053309) unstable; urgency=low
+
+  * fix absolute path in debian/dirs, add Suggests for slim-altusmetrum
+  * change home URL in control file to be the AltOS page
+
+ -- Bdale Garbee <bdale@gag.com>  Thu, 26 Nov 2009 11:15:47 -0700
+
+altos (0.6+43+gd6ba07e) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Decode HDOP data from skytraq GPS
+  * Document ao-postflight --gps and --kml options.
+  * Make ao-postflight create filenames using input filenames.
+  * Make TD print 0000-00-00 for invalid dates.
+  * Fix --plot arg handling. Add -all option.
+  * Convert telemetry file GPS satellite information in cc_log_read
+  * Don't crash if --plot isn't passed on ao-postflight command line
+  * ao-postflight: compute barometric alt for each GPS position
+  * ao-postflight: don't try to use missing gps sat data
+  * Automatically extract flight number for eeprom and telem filenames.
+
+ -- Bdale Garbee <bdale@gag.com>  Sun, 22 Nov 2009 10:18:26 -0700
+
+altos (0.6+32+g87e6f3e) unstable; urgency=low
+
+  [ Bdale Garbee ]
+  * fix typo in comment
+
+  [ Keith Packard ]
+  * Reduce igniter firing time from 500ms to 50ms.
+  * Add GPS date/time output to ao-postflight.
+  * Eliminate SiRF state values from ao-view.
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 20 Nov 2009 13:18:06 -0700
+
+altos (0.6+27+gb0d7e3f) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Add two-point accelerometer calibration.
+  * Remove "l" command as ao-dumplong no longer uses it
+  * Remove "d" command
+  * Remove "f" command
+  * Reformat ADC values to show all 16 bits
+  * ao_flight_test was using accel value for pressure too
+  * Pass accel calibration over telemetry stream. Telemetry data format
+    change.
+  * Add ability to dump eeprom data over radio link.
+  * In packet master, move USB flush from packet thread to echo thread
+  * In USB pollchar, wait for packet before re-checking USB out len
+  * Explicitly use USB I/O routines in packet code
+  * Flush pending input when switching to remote packet mode
+  * Stop recording in ao-dumplog after receiving an invalid block
+  * Move ao_match_word from ao_ignite.c to ao_cmd.c
+  * Add Watchdog Timer Control register definitions
+  * Add reboot command.
+  * Return radio to telemetry settings when packet system closed.
+  * ao-postflight: fix sloppy gps sat data realloc code (was crashing).
+  * Loosen tolerances for main->landed transition
+  * Provide a dummy 'uninstall' target in the src directory.
+  * Share telemetry parsing code in cc library.
+  * Enable telemetry receive in ao_view
+  * Switch order of serial/flight in ao-postflight summary
+  * Add flight number to telemetry stream.
+  * Add date to GPS data, captured from GPRMC packet.
+  * Stop using SiRF state info.
+
+ -- Bdale Garbee <bdale@gag.com>  Fri, 20 Nov 2009 11:43:56 -0700
+
+altos (0.6) unstable; urgency=low
+
+  [ Keith Packard ]
+  * Add support for the SkyTraq GPS unit
+  * Build two versions of TM, one for SiRF, one for SkyTraq
+  * Save some DSEG space by marking cmd functions __reentrant
+  * Add ao_wake_task and ao_exit
+  * Add keyhole-markup generation for ao-postflight.
+  * Initial packet bits. Just testing transmission
+  * Add radio carrier command
+  * Wait for TX to finish sending data
+  * Add ao_alarm
+  * Remove reason from ao_dma_abort
+  * Switch packet code from timer thread to ao_alarm
+  * Use ao_radio_done to wait for TX to completely finish with packet
+  * Send SYN packet to set sequence numbers
+  * Add RFIM register
+  * Do more flushing in packet test code
+  * Use ao_alarm for ao_delay so it can be easily interrupted
+  * Poke master to speed up packet rate when things are busy
+  * No need to wakeup &ao_tick_count now
+  * Enable packet-based communcation to command processor
+  * Add more docs to the README file
+  * Disable interrupts while removing tasks from task list
+  * Add ao_usb_pollchar to ao.h
+
+ -- Bdale Garbee <bdale@gag.com>  Mon, 02 Nov 2009 16:53:45 -0700
+
+altos (0.5+90+g127c312) unstable; urgency=low
+
+  * de-version the libreadline-dev build dependency
+
+ -- Bdale Garbee <bdale@gag.com>  Mon, 02 Nov 2009 15:57:29 -0700
+
+altos (0.5+86+g2de548f) unstable; urgency=low
+
+  * automate push of updated and tagged master branch during
+    debian/rules prebuild
+  * flush repetitive junk out of debian/changelog, and update the
+    prebuild target
+  * add support for tagging git repository on each Debian package build
+
+ -- Bdale Garbee <bdale@gag.com>  Mon, 12 Oct 2009 16:54:28 -0600
+
+altos (0.5+77+gc57bd7f) unstable; urgency=low
+
+  * build for Debian from git
 
-  * Initial packaging
+ -- Bdale Garbee <bdale@gag.com>  Mon, 12 Oct 2009 15:57:19 -0600
 
- -- Bdale Garbee <bdale@gag.com>  Tue, 18 Aug 2009 11:43:43 -0600
index 3aa6d5ddf772fac04ea17b6ebe073d73ae33b960..dec8481f62051c9a66ede507c9857b68a9374d4a 100644 (file)
@@ -1,17 +1,22 @@
 Source: altos
-Section: otherosfs
-Priority: extra
+Section: electronics
+Priority: optional
 Maintainer: Bdale Garbee <bdale@gag.com>
 Uploaders: Keith Packard <keithp@keithp.com>
-Build-Depends: debhelper (>= 7), autoconf, automake, flite1-dev, libasound2-dev, libgconf2-dev, libglade2-dev, libgtk2.0-dev, libreadline5-dev, libusb-1.0-0-dev, nickle, sdcc
-Standards-Version: 3.8.3
-Homepage: http://altusmetrum.org/
+Build-Depends: debhelper (>= 7), autoconf, automake, gawk, libreadline-dev, libusb-1.0-0-dev, nickle, cc1111, xsltproc, fop, docbook-xml, docbook-xsl, swig, default-jdk, freetts, libtool, libjfreechart-java, libbluetooth-dev, pkg-config, libelf-dev, libbluetooth-dev
+Standards-Version: 3.9.3
+Homepage: http://altusmetrum.org/AltOS
+Vcs-Git: git://git.gag.com/fw/altos
+Vcs-Browser: http://git.gag.com/?p=fw/altos
 
 Package: altos
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, nickle
+Depends: ${shlibs:Depends}, ${misc:Depends}, default-jre | java2-runtime, freetts, nickle, libjfreechart-java
+Suggests: slim | gdm
+Replaces: altusmetrum-themes, slim-altusmetrum
+Conflicts: altusmetrum-themes, slim-altusmetrum
 Description: Altus Metrum firmware and utilities
- Firmware and utilities needed to support the TeleMetrum dual-deploy
- recording altimeter for high power model rocketry.  
+ Firmware and utilities needed to support high power model rocketry products
+ from Altus Metrum, including TeleMetrum, TeleMini, and TeleDongle.
  .
  See http://altusmetrum.org/ for more information.
index 7d9a469e7a844393f9708a41974f8d43e9b1b6ad..55841bcae7fb0d881c6fd0e4d6a507e5128491f2 100644 (file)
@@ -10,8 +10,23 @@ Upstream Authors:
 
 Copyright:
 
-    Copyright © 2009 Keith Packard <keithp@keithp.com>
+    The overall AltOS package is
 
+        Copyright © 2009-2011 Keith Packard <keithp@keithp.com>
+
+    Significant contributions include
+
+        Copyright © 2009-2011 Bdale Garbee <bdale@gag.com>
+        Copyright © 2009-2011 Anthony Towns <aj@erisian.com.au>
+
+    Some files incorporated from other projects carry other copyrights:
+
+        Copyright © 2001, 2002, 2003 Jan Kiszka (Jan.Kiszka@web.de)
+       Copyright © 1991-1995 Paul J Turner, Portland, OR
+       Copyright © 1996-2000 Grace Development Team
+       Copyright © 1984, 1987, 1989, 1995 Stephen L. Moshier
+       Copyright © 2003 Maarten Brock, sourceforge.brock@dse.nl
 License:
 
     This program is free software; you can redistribute it and/or modify
@@ -29,7 +44,7 @@ License:
 
 The Debian packaging is:
 
-    Copyright (C) 2009 Bdale Garbee <bdale@gag.com>
+    Copyright © 2009-2011 Bdale Garbee <bdale@gag.com>
 
 and is also licensed under the GPL version 2.
 
index 8b9a4657b321055217265496916e657bd9924562..c7f7918e2e388eecfbe1e8034922c7723bd100e9 100644 (file)
@@ -1,3 +1,8 @@
-etc/apt/sources.list.d
 usr/bin
-usr/share/altos
+usr/lib/altos
+usr/share/altos/themes
+usr/share/applications
+usr/share/gdm/themes/altusmetrum
+usr/share/java
+usr/share/pixmaps
+usr/share/slim/themes/altusmetrum
index 50bd824bb7bc7edba72e31d1f7a6ef7932846902..3ac75ad424cc8c6a23b9019db8c4cfd2c9d494e0 100644 (file)
@@ -1,2 +1,10 @@
 NEWS
 README
+doc/altusmetrum.html
+doc/altusmetrum.pdf
+doc/telemetry.html
+doc/telemetry.pdf
+doc/altos.html
+doc/altos.pdf
+doc/telemetrum-outline.pdf
+doc/megametrum-outline.pdf
index 7e15c9c8657d627be1e1e624ee5121b095167622..b479ee5e340243fef04aa91a998fc7990b25541c 100644 (file)
@@ -1,2 +1,6 @@
-?package(altos):needs="X11" section="Applications/Viewers"\
-  title="aoview" command="/usr/bin/aoview"
+?package(altos): \
+       needs="X11" \
+       section="Applications/Viewers" \
+       title="AltOS UI" \
+       command="/usr/bin/altosui" \
+       icon="/usr/share/pixmaps/altusmetrum.xpm"
index 078dac56cddff93a9e91161cd046e1181d255b42..a4cbe2c112147f66a3078cc62f9c6dba11c207e6 100755 (executable)
@@ -6,10 +6,7 @@ PKG_VERSION := $(shell dpkg-parsechangelog | sed -ne 's/^Version: \(.*\)/\1/p')
 
 # this target invoked by git-buildpackage using a clean hook, see .gbp.conf
 prebuild:
-       dch -v `git describe | tr - +` "build for Debian from git"
-       git log > ChangeLog
-       git commit ChangeLog debian/changelog \
-               -m "update changelogs for Debian build"
+       echo "not frobbing changelog for official builds"
 
 configure: configure-stamp
 configure-stamp:
@@ -17,11 +14,14 @@ configure-stamp:
        ./autogen.sh --prefix=/usr
        touch configure-stamp
 
-build: build-stamp
+build: build-arch build-indep
+build-arch: build-stamp
+build-indep: build-stamp
 
 build-stamp: configure-stamp  
        dh_testdir
        $(MAKE) VERSION=$(PKG_VERSION)
+       (cd doc ; $(MAKE))
        touch $@
 
 clean: 
@@ -38,12 +38,10 @@ install: build
        dh_installdirs
 
        $(MAKE) DESTDIR=$(CURDIR)/debian/altos install
+       sed -i "/dependency_libs/ s/'.*'/''/" `find debian/altos/usr/lib/altos/ -name '*.la'`
 
-# Build architecture-independent files here.
 binary-indep: install
-# We have nothing to do by default.
 
-# Build architecture-dependent files here.
 binary-arch: install
        dh_testdir
        dh_testroot
@@ -51,7 +49,11 @@ binary-arch: install
        dh_installdocs
        dh_installexamples
        dh_install
-#      dh_installmenu
+       ln -s /usr/share/altos/themes/background.png \
+               debian/altos/usr/share/gdm/themes/altusmetrum/background.png
+       ln -s /usr/share/altos/themes/background.png \
+               debian/altos/usr/share/slim/themes/altusmetrum/background.png
+       dh_installmenu
 #      dh_installdebconf
 #      dh_installlogrotate
 #      dh_installemacsen
diff --git a/debian/source/format b/debian/source/format
new file mode 100644 (file)
index 0000000..163aaf8
--- /dev/null
@@ -0,0 +1 @@
+3.0 (quilt)
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644 (file)
index 0000000..54ca39b
--- /dev/null
@@ -0,0 +1,3 @@
+*.html
+*.pdf
+*.fo
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644 (file)
index 0000000..fbe8bc1
--- /dev/null
@@ -0,0 +1,57 @@
+#
+#      http://docbook.sourceforge.net/release/xsl/current/README
+#
+
+RELNOTES=\
+       release-notes-0.7.1.html \
+       release-notes-0.8.html \
+       release-notes-0.9.html \
+       release-notes-0.9.2.html \
+       release-notes-1.0.1.html \
+       release-notes-1.1.html
+
+RELNOTES_XSL=$(RELNOTES:.html=.xsl)
+HTML=altusmetrum.html altos.html telemetry.html companion.html $(RELNOTES)
+PDF=altusmetrum.pdf altos.pdf telemetry.pdf companion.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=
+
+.SUFFIXES: .xsl .html .fo .pdf
+
+XSLTFLAGS=--stringparam section.autolabel 1 --xinclude
+
+.xsl.html:
+       xsltproc $(XSLTFLAGS) -o $@ $(HTMLSTYLE) $*.xsl
+
+.xsl.fo:
+       xsltproc $(XSLTFLAGS) -o $@ $(FOSTYLE) $*.xsl
+
+.fo.pdf:
+       fop -fo $*.fo -pdf $@
+
+all:   $(HTML) $(PDF)
+
+install:       all
+
+publish:       $(DOC)
+       cp $(DOC) /home/bdale/web/altusmetrum/AltOS/doc/
+       (cd /home/bdale/web/altusmetrum ; \
+        git add /home/bdale/web/altusmetrum/AltOS/doc/* ; \
+        echo "update docs" | \
+        git commit -F - /home/bdale/web/altusmetrum/AltOS/doc/* ; \
+        git push)
+
+clean:
+       rm -f $(HTML) $(PDF) *.fo
+
+distclean:
+       rm -f $(HTML) $(PDF) *.fo
+
+altusmetrum.html: $(RELNOTES_XSL)
+altusmetrum.fo: $(RELNOTES_XSL)
+
+indent:                altusmetrum.xsl
+       xmlindent -i 2 < altusmetrum.xsl > altusmetrum.new
+
diff --git a/doc/altos.xsl b/doc/altos.xsl
new file mode 100644 (file)
index 0000000..37bb58d
--- /dev/null
@@ -0,0 +1,1291 @@
+<?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>
+      <section>
+       <title>__data</title>
+       <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>
+      </section>
+      <section>
+       <title>__idata</title>
+       <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>
+      </section>
+      <section>
+       <title>__xdata</title>
+       <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>
+      </section>
+      <section>
+       <title>__pdata</title>
+       <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>
+      </section>
+      <section>
+       <title>__code</title>
+       <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>
+      </section>
+      <section>
+       <title>__bit</title>
+       <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>
+      </section>
+      <section>
+       <title>__sfr, __sfr16, __sfr32, __sbit</title>
+       <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>
+      </section>
+    </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>
+    <section>
+      <title>ao_add_task</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_exit</title>
+      <programlisting>
+       void
+       ao_exit(void)
+      </programlisting>
+      <para>
+       This terminates the current task.
+      </para>
+    </section>
+    <section>
+      <title>ao_sleep</title>
+      <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>
+      <para>
+       Because ao_wakeup wakes every task waiting on a particular
+       location, ao_sleep should be used in a loop that first
+       checks the desired condition, blocks in ao_sleep and then
+       rechecks until the condition is satisfied. If the
+       location may be signaled from an interrupt handler, the
+       code will need to block interrupts by using the __critical
+       label around the block of code. Here's a complete example:
+       <programlisting>
+         __critical while (!ao_radio_done)
+                 ao_sleep(&amp;ao_radio_done);
+       </programlisting>
+      </para>
+    </section>
+    <section>
+      <title>ao_wakeup</title>
+      <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. Here's an example of using this:
+       <programlisting>
+         if (RFIF &amp; RFIF_IM_DONE) {
+                 ao_radio_done = 1;
+                 ao_wakeup(&amp;ao_radio_done);
+                 RFIF &amp;= ~RFIF_IM_DONE;
+         }
+       </programlisting>
+       Note that this need not be enclosed in __critical as the
+       ao_sleep block can only be run from normal mode, and so
+       this sequence can never be interrupted with execution of
+       the other sequence.
+      </para>
+    </section>
+    <section>
+      <title>ao_alarm</title>
+      <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.
+       <programlisting>
+         ao_alarm(ao_packet_master_delay);
+         __critical while (!ao_radio_dma_done)
+                 if (ao_sleep(&amp;ao_radio_dma_done) != 0)
+                         ao_radio_abort();
+       </programlisting>
+       In this example, a timeout is set before waiting for
+       incoming radio data. If no data is received before the
+       timeout fires, ao_sleep will return 1 and then this code
+       will abort the radio receive operation.
+      </para>
+    </section>
+    <section>
+      <title>ao_start_scheduler</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_clock_init</title>
+      <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>
+    </section>
+  </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>
+    <section>
+      <title>ao_time</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_delay</title>
+      <programlisting>
+       void
+       ao_delay(uint16_t ticks);
+      </programlisting>
+      <para>
+       Suspend the current task for at least 'ticks' clock units.
+      </para>
+    </section>
+    <section>
+      <title>ao_timer_set_adc_interval</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_timer_init</title>
+      <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>
+    </section>
+  </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>
+    <section>
+      <title>ao_mutex_get</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_mutex_put</title>
+      <programlisting>
+       void
+       ao_mutex_put(__xdata uint8_t *mutex);
+      </programlisting>
+      <para>
+       Releases the specified mutex, waking up all tasks waiting
+       for it.
+      </para>
+    </section>
+  </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>
+    <section>
+      <title>ao_dma_alloc</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_dma_set_transfer</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_dma_start</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_dma_trigger</title>
+      <programlisting>
+       void
+       ao_dma_trigger(uint8_t id)
+      </programlisting>
+      <para>
+       Trigger the specified DMA engine to start copying data.
+      </para>
+    </section>
+    <section>
+      <title>ao_dma_abort</title>
+      <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>
+    </section>
+  </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>
+    <section>
+      <title>putchar</title>
+      <programlisting>
+       void
+       putchar(char c)
+      </programlisting>
+      <para>
+       Delivers a single character to the current console
+       device.
+      </para>
+    </section>
+    <section>
+      <title>getchar</title>
+      <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>
+    </section>
+    <section>
+      <title>flush</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_add_stdio</title>
+      <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(&amp;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>
+    </section>
+  </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>
+    <section>
+      <title>ao_cmd_register</title>
+      <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>
+           <title>ao_cmd_success</title>
+           <listitem>
+             <para>
+               The command was parsed successfully. There is no
+               need to assign this value, it is the default.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <title>ao_cmd_lex_error</title>
+           <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>
+           <title>ao_syntax_error</title>
+           <listitem>
+             <para>
+               The command line is invalid for some reason other
+               than invalid tokens.
+             </para>
+           </listitem>
+         </varlistentry>
+       </variablelist>
+      </para>
+    </section>
+    <section>
+      <title>ao_cmd_lex</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_cmd_put16</title>
+      <programlisting>
+       void
+       ao_cmd_put16(uint16_t v);
+      </programlisting>
+      <para>
+       Writes 'v' as four hexadecimal characters.
+      </para>
+    </section>
+    <section>
+      <title>ao_cmd_put8</title>
+      <programlisting>
+       void
+       ao_cmd_put8(uint8_t v);
+      </programlisting>
+      <para>
+       Writes 'v' as two hexadecimal characters.
+      </para>
+    </section>
+    <section>
+      <title>ao_cmd_white</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_cmd_hex</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_cmd_decimal</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_match_word</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_cmd_init</title>
+      <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>
+    </section>
+  </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>
+    <section>
+      <title>ao_usb_flush</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_usb_putchar</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_usb_pollchar</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_usb_getchar</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_usb_disable</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_usb_enable</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_usb_init</title>
+      <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>
+    </section>
+  </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>
+    <section>
+      <title>ao_serial_getchar</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_serial_putchar</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_serial_drain</title>
+      <programlisting>
+       void
+       ao_serial_drain(void);
+      </programlisting>
+      <para>
+       Blocks until the transmit fifo is empty. Used internally
+       when changing serial speeds.
+      </para>
+    </section>
+    <section>
+      <title>ao_serial_set_speed</title>
+      <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>
+    </section>
+    <section>
+      <title>ao_serial_init</title>
+      <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>
+    </section>
+  </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>
+    <section>
+      <title>ao_radio_set_telemetry</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_radio_set_packet</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_radio_set_rdf</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_radio_idle</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_radio_get</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_radio_put</title>
+       <programlisting>
+         void
+         ao_radio_put(void);
+       </programlisting>
+       <para>
+         Releases the radio mutex.
+       </para>
+    </section>
+    <section>
+      <title>ao_radio_abort</title>
+       <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>
+    </section>
+    <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>
+    <section>
+      <title>ao_radio_send</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_radio_recv</title>
+       <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>
+    </section>
+    <para>
+      In radio direction finding mode, there's just one function to
+      use
+    </para>
+    <section>
+      <title>ao_radio_rdf</title>
+       <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>
+    </section>
+    <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>
+    <section>
+      <title>ao_packet_putchar</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_packet_pollchar</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_packet_slave_start</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_packet_slave_stop</title>
+       <programlisting>
+         void
+         ao_packet_slave_stop(void);
+       </programlisting>
+       <para>
+         Disables the packet slave task, stopping the radio receiver.
+       </para>
+    </section>
+    <section>
+      <title>ao_packet_slave_init</title>
+       <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>
+    </section>
+    <section>
+      <title>ao_packet_master_init</title>
+       <programlisting>
+         void
+         ao_packet_master_init(void);
+       </programlisting>
+       <para>
+         Adds the 'p' packet forward command to start packet mode.
+       </para>
+    </section>
+  </chapter>
+</book>
diff --git a/doc/altusmetrum.xsl b/doc/altusmetrum.xsl
new file mode 100644 (file)
index 0000000..8339ca4
--- /dev/null
@@ -0,0 +1,2495 @@
+<?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>The Altus Metrum System</title>
+  <subtitle>An Owner's Manual for TeleMetrum, TeleMini and TeleDongle Devices</subtitle>
+  <bookinfo>
+    <author>
+      <firstname>Bdale</firstname>
+      <surname>Garbee</surname>
+    </author>
+    <author>
+      <firstname>Keith</firstname>
+      <surname>Packard</surname>
+    </author>
+    <author>
+      <firstname>Bob</firstname>
+      <surname>Finch</surname>
+    </author>
+    <author>
+      <firstname>Anthony</firstname>
+      <surname>Towns</surname>
+    </author>
+    <copyright>
+      <year>2011</year>
+      <holder>Bdale Garbee and 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>1.0</revnumber>
+        <date>24 August 2011</date>
+       <revremark>
+         Updated for software version 1.0.  Note that 1.0 represents a
+         telemetry format change, meaning both ends of a link 
+         (TeleMetrum/TeleMini and TeleDongle) must be updated or 
+          communications will fail.
+       </revremark>
+      </revision>
+      <revision>
+        <revnumber>0.9</revnumber>
+        <date>18 January 2011</date>
+       <revremark>
+         Updated for software version 0.9.  Note that 0.9 represents a
+         telemetry format change, meaning both ends of a link (TeleMetrum and
+         TeleDongle) must be updated or communications will fail.
+       </revremark>
+      </revision>
+      <revision>
+        <revnumber>0.8</revnumber>
+        <date>24 November 2010</date>
+       <revremark>Updated for software version 0.8 </revremark>
+      </revision>
+    </revhistory>
+  </bookinfo>
+  <acknowledgements>
+    <para>
+      Thanks to Bob Finch, W9YA, NAR 12965, TRA 12350 for writing "The
+      Mere-Mortals Quick Start/Usage Guide to the Altus Metrum Starter
+      Kit" which formed the basis of the original Getting Started chapter 
+      in this manual.  Bob was one of our first customers for a production
+      TeleMetrum, and his continued enthusiasm and contributions
+      are immensely gratifying and highly appreciated!
+    </para>
+    <para>
+      And thanks to Anthony (AJ) Towns for major contributions including
+      the AltosUI graphing and site map code and associated documentation. 
+      Free software means that our customers and friends can become our
+      collaborators, and we certainly appreciate this level of
+      contribution!
+    </para>
+    <para>
+      Have fun using these products, and we hope to meet all of you
+      out on the rocket flight line somewhere.
+      <literallayout>
+Bdale Garbee, KB0G
+NAR #87103, TRA #12201
+
+Keith Packard, KD7SQG
+NAR #88757, TRA #12200
+      </literallayout>
+    </para>
+  </acknowledgements>
+  <chapter>
+    <title>Introduction and Overview</title>
+    <para>
+      Welcome to the Altus Metrum community!  Our circuits and software reflect
+      our passion for both hobby rocketry and Free Software.  We hope their
+      capabilities and performance will delight you in every way, but by
+      releasing all of our hardware and software designs under open licenses,
+      we also hope to empower you to take as active a role in our collective
+      future as you wish!
+    </para>
+    <para>
+      The first device created for our community was TeleMetrum, a dual
+      deploy altimeter with fully integrated GPS and radio telemetry
+      as standard features, and a "companion interface" that will
+      support optional capabilities in the future.
+    </para>
+    <para>
+      The newest device is TeleMini, a dual deploy altimeter with
+      radio telemetry and radio direction finding. This device is only
+      13mm by 38mm (½ inch by 1½ inches) and can fit easily in an 18mm 
+      air-frame.
+    </para>
+    <para>
+      Complementing TeleMetrum and TeleMini is TeleDongle, a USB to RF 
+      interface for communicating with the altimeters.  Combined with your 
+      choice of antenna and
+      notebook computer, TeleDongle and our associated user interface software
+      form a complete ground station capable of logging and displaying in-flight
+      telemetry, aiding rocket recovery, then processing and archiving flight
+      data for analysis and review.
+    </para>
+    <para>
+      More products will be added to the Altus Metrum family over time, and
+      we currently envision that this will be a single, comprehensive manual
+      for the entire product family.
+    </para>
+  </chapter>
+  <chapter>
+    <title>Getting Started</title>
+    <para>
+      The first thing to do after you check the inventory of parts in your
+      "starter kit" is to charge the battery.
+    </para>
+    <para>
+      The TeleMetrum battery can be charged 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
+      TeleMetrum circuitry will charge the battery whenever it is plugged
+      in, because the TeleMetrum's on-off switch does NOT control the
+      charging circuitry.
+    </para>
+    <para>
+      When the GPS chip is initially searching for
+      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
+      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.
+    </para>
+    <para>
+      The TeleMini battery can be charged by disconnecting it from the
+      TeleMini board and plugging it into a standalone battery charger 
+      board, and connecting that via a USB cable to a laptop or other USB
+      power source
+    </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
+      "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.  We therefore recommend installing
+      our software before plugging in TeleDongle if you are using a Windows
+      computer.  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.
+    </para>
+    <para>
+      Next you should obtain and install the AltOS software.  These include
+      the AltosUI ground station program, current firmware images for
+      TeleMetrum, TeleMini and TeleDongle, and a number of standalone 
+      utilities that are rarely needed.  Pre-built binary packages are 
+      available for Linux, Microsoft Windows, and recent MacOSX versions.  
+      Full source code and build instructions are also available.
+      The latest version may always be downloaded from
+      <ulink url="http://altusmetrum.org/AltOS"/>.
+    </para>
+  </chapter>
+  <chapter>
+    <title>Handling Precautions</title>
+    <para>
+      All Altus Metrum products are sophisticated electronic devices.  
+      When handled gently and properly installed in an air-frame, they
+      will deliver impressive results.  However, as with all electronic 
+      devices, there are some precautions you must take.
+    </para>
+    <para>
+      The Lithium Polymer rechargeable batteries have an
+      extraordinary power density.  This is great because we can fly with
+      much less battery mass than if we used alkaline batteries or previous
+      generation rechargeable batteries... but if they are punctured
+      or their leads are allowed to short, they can and will release their
+      energy very rapidly!
+      Thus we recommend that you take some care when handling our batteries
+      and consider giving them some extra protection in your air-frame.  We
+      often wrap them in suitable scraps of closed-cell packing foam before
+      strapping them down, for example.
+    </para>
+    <para>
+      The barometric sensors used on both TeleMetrum and TeleMini are 
+      sensitive to sunlight.  In normal TeleMetrum mounting situations, it 
+      and all of the other surface mount components
+      are "down" towards whatever the underlying mounting surface is, so
+      this is not normally a problem.  Please consider this, though, when
+      designing an installation, for example, in an air-frame with a
+      see-through plastic payload bay.  It is particularly important to
+      consider this with TeleMini, both because the baro sensor is on the
+      "top" of the board, and because many model rockets with payload bays
+      use clear plastic for the payload bay!  Replacing these with an opaque
+      cardboard tube, painting them, or wrapping them with a layer of masking
+      tape are all reasonable approaches to keep the sensor out of direct
+      sunlight.
+    </para>
+    <para>
+      The barometric sensor sampling port must be able to "breathe",
+      both by not being covered by foam or tape or other materials that might
+      directly block the hole on the top of the sensor, and also by having a
+      suitable static vent to outside air.
+    </para>
+    <para>
+      As with all other rocketry electronics, Altus Metrum altimeters must 
+      be protected from exposure to corrosive motor exhaust and ejection 
+      charge gasses.
+    </para>
+  </chapter>
+  <chapter>
+    <title>Hardware Overview</title>
+    <para>
+      TeleMetrum is a 1 inch by 2.75 inch circuit board.  It was designed to
+      fit inside coupler for 29mm air-frame tubing, but using it in a tube that
+      small in diameter may require some creativity in mounting and wiring
+      to succeed!  The presence of an accelerometer means TeleMetrum should
+      be aligned along the flight axis of the airframe, and by default the 1/4
+      wave UHF wire antenna should be on the nose-cone end of the board.  The
+      antenna wire is about 7 inches long, and wiring for a power switch and
+      the e-matches for apogee and main ejection charges depart from the
+      fin can end of the board, meaning an ideal "simple" avionics
+      bay for TeleMetrum should have at least 10 inches of interior length.
+    </para>
+    <para>
+      TeleMini is a 0.5 inch by 1.5 inch circuit board.   It was designed to
+      fit inside an 18mm air-frame tube, but using it in a tube that
+      small in diameter may require some creativity in mounting and wiring
+      to succeed!  Since there is no accelerometer, TeleMini can be mounted
+      in any convenient orientation.  The default 1/4
+      wave UHF wire antenna attached to the center of one end of
+      the board is about 7 inches long, and wiring for a power switch and
+      the e-matches for apogee and main ejection charges depart from the
+      other end of the board, meaning an ideal "simple" avionics
+      bay for TeleMini should have at least 9 inches of interior length.
+    </para>
+    <para>
+      A typical TeleMetrum or TeleMini installation involves attaching 
+      only a suitable Lithium Polymer battery, a single pole switch for 
+      power on/off, and two pairs of wires connecting e-matches for the 
+      apogee and main ejection charges.  All Altus Metrum products are 
+      designed for use with single-cell batteries with 3.7 volts nominal.
+    </para>
+    <para>
+      By default, we use the unregulated output of the Li-Po battery directly
+      to fire ejection charges.  This works marvelously with standard
+      low-current e-matches like the J-Tek from MJG Technologies, and with
+      Quest Q2G2 igniters.  However, if you want or need to use a separate 
+      pyro battery, check out the "External Pyro Battery" section in this 
+      manual for instructions on how to wire that up. The altimeters are 
+      designed to work with an external pyro battery of no more than 15 volts.
+    </para>
+    <para>
+      Ejection charges are wired directly to the screw terminal block
+      at the aft end of the altimeter.  You'll need a very small straight 
+      blade screwdriver for these screws, such as you might find in a 
+      jeweler's screwdriver set.
+    </para>
+    <para>
+      TeleMetrum also uses the screw terminal block for the power
+      switch leads. On TeleMini, the power switch leads are soldered
+      directly to the board and can be connected directly to a switch.
+    </para>
+    <para>
+      For most air-frames, the integrated antennas are more than
+      adequate.   However, if you are installing in a carbon-fiber or
+      metal electronics bay which is opaque to RF signals, you may need to
+      use off-board external antennas instead.  In this case, you can
+      order an altimeter with an SMA connector for the UHF antenna
+      connection, and, on TeleMetrum, you can unplug the integrated GPS
+      antenna and select an appropriate off-board GPS antenna with
+      cable terminating in a U.FL connector.
+    </para>
+  </chapter>
+  <chapter>
+    <title>System Operation</title>
+    <section>
+      <title>Firmware Modes </title>
+      <para>
+        The AltOS firmware build for the altimeters has two
+        fundamental modes, "idle" and "flight".  Which of these modes
+        the firmware operates in is determined at start up time. For
+        TeleMetrum, the mode is controlled by the orientation of the
+        rocket (well, actually the board, of course...) at the time
+        power is switched on.  If the rocket is "nose up", then
+        TeleMetrum assumes it's on a rail or rod being prepared for
+        launch, so the firmware chooses flight mode.  However, if the
+        rocket is more or less horizontal, the firmware instead enters
+        idle mode.  Since TeleMini doesn't have an accelerometer we can
+        use to determine orientation, "idle" mode is selected when the
+        board receives a command packet within the first five seconds
+        of operation; if no packet is received, the board enters
+        "flight" mode.
+      </para>
+      <para>
+        At power on, you will hear three beeps or see three flashes
+        ("S" in Morse code for start up) and then a pause while
+        the altimeter completes initialization and self test, and decides 
+       which mode to enter next.
+      </para>
+      <para>
+        In flight or "pad" mode, the altimeter engages the flight
+        state machine, goes into transmit-only mode to
+        send telemetry, and waits for launch to be detected.
+        Flight mode is indicated by an "di-dah-dah-dit" ("P" for pad)
+        on the beeper or lights, followed by beeps or flashes
+        indicating the state of the pyrotechnic igniter continuity.
+        One beep/flash indicates apogee continuity, two beeps/flashes
+        indicate main continuity, three beeps/flashes indicate both
+        apogee and main continuity, and one longer "brap" sound or
+        rapidly alternating lights indicates no continuity.  For a
+        dual deploy flight, make sure you're getting three beeps or
+        flashes before launching!  For apogee-only or motor eject
+        flights, do what makes sense.
+      </para>
+      <para>
+        If idle mode is entered, you will hear an audible "di-dit" or see 
+        two short flashes ("I" for idle), and the flight state machine is 
+        disengaged, thus no ejection charges will fire.  The altimeters also 
+        listen for the radio link when in idle mode for requests sent via 
+        TeleDongle.  Commands can be issued to a TeleMetrum in idle mode 
+        over either
+        USB or the radio link equivalently. TeleMini only has the radio link.
+        Idle mode is useful for configuring the altimeter, for extracting data
+        from the on-board storage chip after flight, and for ground testing
+        pyro charges.
+      </para>
+      <para>
+        One "neat trick" of particular value when TeleMetrum is used with 
+        very large air-frames, is that you can power the board up while the 
+        rocket is horizontal, such that it comes up in idle mode.  Then you can
+        raise the air-frame to launch position, and issue a 'reset' command 
+       via TeleDongle over the radio link to cause the altimeter to reboot and 
+        come up in flight mode.  This is much safer than standing on the top 
+        step of a rickety step-ladder or hanging off the side of a launch 
+        tower with a screw-driver trying to turn on your avionics before 
+        installing igniters!
+      </para>
+    </section>
+    <section>
+      <title>GPS </title>
+      <para>
+        TeleMetrum includes a complete GPS receiver.  A complete explanation 
+        of how GPS works is beyond the scope of this manual, but the bottom 
+        line is that the TeleMetrum GPS receiver needs to lock onto at least 
+        four satellites to obtain a solid 3 dimensional position fix and know
+        what time it is.
+      </para>
+      <para>
+        TeleMetrum provides backup power to the GPS chip any time a 
+        battery is connected.  This allows the receiver to "warm start" on
+        the launch rail much faster than if every power-on were a GPS 
+       "cold start".  In typical operations, powering up TeleMetrum
+        on the flight line in idle mode while performing final air-frame
+        preparation will be sufficient to allow the GPS receiver to cold
+        start and acquire lock.  Then the board can be powered down during
+        RSO review and installation on a launch rod or rail.  When the board
+        is turned back on, the GPS system should lock very quickly, typically
+        long before igniter installation and return to the flight line are
+        complete.
+      </para>
+    </section>
+    <section>
+      <title>Controlling An Altimeter Over The Radio Link</title>
+      <para>
+        One of the unique features of the Altus Metrum system is
+        the ability to create a two way command link between TeleDongle
+        and an altimeter using the digital radio transceivers built into
+        each device. This allows you to interact with the altimeter 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 radio
+        link. TeleMini doesn't provide a USB connector and so it is
+        always communicated with over radio.  Select the appropriate 
+        TeleDongle device when the list of devices is presented and 
+        AltosUI will interact with an altimeter over the radio link.
+      </para>
+      <para>
+       One oddity in the current interface is how AltosUI selects the
+       frequency for radio communications. Instead of providing
+       an interface to specifically configure the frequency, it uses
+       whatever frequency was most recently selected for the target
+       TeleDongle device in Monitor Flight mode. If you haven't ever
+       used that mode with the TeleDongle in question, select the
+       Monitor Flight button from the top level UI, and pick the
+       appropriate TeleDongle device.  Once the flight monitoring
+       window is open, select the desired frequency and then close it
+       down again. All radio communications will now use that frequency.
+      </para>
+      <itemizedlist>
+        <listitem>
+          <para>
+            Save Flight Data—Recover flight data from the rocket without
+            opening it up.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            Configure altimeter apogee delays or main deploy heights
+            to respond to changing launch conditions. You can also
+            'reboot' the altimeter. Use this to remotely enable the
+            flight computer by turning TeleMetrum on in "idle" mode,
+            then once the air-frame is oriented for launch, you can
+            reboot the altimeter 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 air-frame. Simply assembly the
+            rocket as if for flight with the apogee and main charges
+            loaded, then remotely command the altimeter to fire the
+            igniters.
+          </para>
+        </listitem>
+      </itemizedlist>
+      <para>
+        Operation over the radio link for configuring an altimeter, ground
+        testing igniters, and so forth uses the same RF frequencies as flight
+        telemetry.  To configure the desired TeleDongle frequency, select
+        the monitor flight tab, then use the frequency selector and 
+        close the window before performing other desired radio operations.
+      </para>
+      <para>
+        TeleMetrum only enables radio commanding 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>
+       TeleMini listens for a command packet for five seconds after
+       first being turned on, if it doesn't hear anything, it enters
+       'pad' mode, ready for flight and will no longer listen for
+       command packets. The easiest way to connect to TeleMini is to
+       initiate the command and select the TeleDongle device. At this
+       point, the TeleDongle will be attempting to communicate with
+       the TeleMini. Now turn TeleMini on, and it should immediately
+       start communicating with the TeleDongle and the desired
+       operation can be performed.
+      </para>
+      <para>
+        You can monitor the operation of the radio link by watching the 
+        lights on the devices. The red LED will flash each time a packet
+        is tramsitted, while the green LED will light up on TeleDongle when 
+        it is waiting to receive a packet from the altimeter.
+      </para>
+    </section>
+    <section>
+      <title>Ground Testing </title>
+      <para>
+        An important aspect of preparing a rocket using electronic deployment
+        for flight is ground testing the recovery system.  Thanks
+        to the bi-directional radio link central to the Altus Metrum system,
+        this can be accomplished in a TeleMetrum or TeleMini equipped rocket 
+        with less work than you may be accustomed to with other systems.  It 
+        can even be fun!
+      </para>
+      <para>
+        Just prep the rocket for flight, then power up the altimeter
+        in "idle" mode (placing air-frame horizontal for TeleMetrum or
+        selected the Configure Altimeter tab for TeleMini).  This will cause 
+        the firmware to go into "idle" mode, in which the normal flight
+        state machine is disabled and charges will not fire without
+        manual command.  You can now command the altimeter to fire the apogee
+        or main charges from a safe distance using your computer and 
+        TeleDongle and the Fire Igniter tab to complete ejection testing.
+      </para>
+    </section>
+    <section>
+      <title>Radio Link </title>
+      <para>
+        The chip our boards are based on incorporates an RF transceiver, but
+        it's not a full duplex system... each end can only be transmitting or
+        receiving at any given moment.  So we had to decide how to manage the
+        link.
+      </para>
+      <para>
+        By design, the altimeter firmware listens for the radio link when
+        it's in "idle mode", which
+        allows us to use the radio link to configure the rocket, do things like
+        ejection tests, and extract data after a flight without having to
+        crack open the air-frame.  However, when the board is in "flight
+        mode", the altimeter only
+        transmits and doesn't listen at all.  That's because we want to put
+        ultimate priority on event detection and getting telemetry out of
+        the rocket through
+        the radio in case the rocket crashes and we aren't able to extract
+        data later...
+      </para>
+      <para>
+        We don't use a 'normal packet radio' mode like APRS because they're 
+        just too inefficient.  The GFSK modulation we use is FSK with the
+        base-band pulses passed through a
+        Gaussian filter before they go into the modulator to limit the
+        transmitted bandwidth.  When combined with the hardware forward error
+        correction support in the cc1111 chip, this allows us to have a very
+        robust 38.4 kilobit data link with only 10 milliwatts of transmit 
+        power, a whip antenna in the rocket, and a hand-held Yagi on the 
+        ground.  We've had flights to above 21k feet AGL with great reception, 
+        and calculations suggest we should be good to well over 40k feet AGL 
+        with a 5-element yagi on the ground.  We hope to fly boards to higher 
+        altitudes over time, and would of course appreciate customer feedback 
+        on performance in higher altitude flights!
+      </para>
+    </section>
+    <section>
+      <title>Configurable Parameters</title>
+      <para>
+        Configuring an Altus Metrum altimeter for flight is very
+        simple.  Even on our baro-only TeleMini board, the use of a Kalman 
+        filter means there is no need to set a "mach delay".  The few 
+        configurable parameters can all be set using AltosUI over USB or
+        or radio link via TeleDongle.
+      </para>
+      <section>
+        <title>Radio Frequency</title>
+        <para>
+         Altus Metrum boards support radio frequencies in the 70cm
+         band. By default, the configuration interface provides a
+         list of 10 "standard" frequencies in 100kHz channels starting at
+         434.550MHz.  However, the firmware supports use of
+         any 50kHz multiple within the 70cm band. At any given
+         launch, we highly recommend coordinating when and by whom each
+         frequency will be used to avoid interference.  And of course, both
+         altimeter and TeleDongle must be configured to the same
+         frequency to successfully communicate with each other.
+        </para>
+      </section>
+      <section>
+        <title>Apogee Delay</title>
+        <para>
+          Apogee delay is the number of seconds after the altimeter detects flight
+          apogee that the drogue charge should be fired.  In most cases, this
+          should be left at the default of 0.  However, if you are flying
+          redundant electronics such as for an L3 certification, you may wish
+          to set one of your altimeters to a positive delay so that both
+          primary and backup pyrotechnic charges do not fire simultaneously.
+        </para>
+        <para>
+          The Altus Metrum apogee detection algorithm fires exactly at
+          apogee.  If you are also flying an altimeter like the
+          PerfectFlite MAWD, which only supports selecting 0 or 1
+          seconds of apogee delay, you may wish to set the MAWD to 0
+          seconds delay and set the TeleMetrum to fire your backup 2
+          or 3 seconds later to avoid any chance of both charges
+          firing simultaneously.  We've flown several air-frames this
+          way quite happily, including Keith's successful L3 cert.
+        </para>
+      </section>
+      <section>
+        <title>Main Deployment Altitude</title>
+        <para>
+          By default, the altimeter will fire the main deployment charge at an
+          elevation of 250 meters (about 820 feet) above ground.  We think this
+          is a good elevation for most air-frames, but feel free to change this
+          to suit.  In particular, if you are flying two altimeters, you may
+          wish to set the
+          deployment elevation for the backup altimeter to be something lower
+          than the primary so that both pyrotechnic charges don't fire
+          simultaneously.
+        </para>
+      </section>
+      <section>
+       <title>Maximum Flight Log</title>
+       <para>
+         TeleMetrum version 1.1 and 1.2 have 2MB of on-board flash storage,
+         enough to hold over 40 minutes of data at full data rate
+         (100 samples/second). TeleMetrum 1.0 has 1MB of on-board
+         storage. As data are stored at a reduced rate during descent
+         (10 samples/second), there's plenty of space to store many
+         flights worth of data.
+       </para>
+       <para>
+         The on-board flash is partitioned into separate flight logs,
+         each of a fixed maximum size. Increase the maximum size of
+         each log and you reduce the number of flights that can be
+         stored. Decrease the size and TeleMetrum can store more
+         flights.
+       </para>
+       <para>
+         All of the configuration data is also stored in the flash
+         memory, which consumes 64kB on TeleMetrum v1.1/v1.2 and 256B on
+         TeleMetrum v1.0. This configuration space is not available
+         for storing flight log data.
+       </para>
+       <para>
+         To compute the amount of space needed for a single flight,
+         you can multiply the expected ascent time (in seconds) by
+         800, multiply the expected descent time (in seconds) by 80
+         and add the two together. That will slightly under-estimate
+         the storage (in bytes) needed for the flight. For instance,
+         a flight spending 20 seconds in ascent and 150 seconds in
+         descent will take about (20 * 800) + (150 * 80) = 28000
+         bytes of storage. You could store dozens of these flights in
+         the on-board flash.
+       </para>
+       <para>
+         The default size, 192kB, allows for 10 flights of storage on
+         TeleMetrum v1.1/v1.2 and 5 flights on TeleMetrum v1.0. This
+         ensures that you won't need to erase the memory before
+         flying each time while still allowing more than sufficient
+         storage for each flight.
+       </para>
+       <para>
+         As TeleMini does not contain an accelerometer, it stores
+         data at 10 samples per second during ascent and one sample
+         per second during descent. Each sample is a two byte reading
+         from the barometer. These are stored in 5kB of
+         on-chip flash memory which can hold 256 seconds at the
+         ascent rate or 2560 seconds at the descent rate. Because of
+         the limited storage, TeleMini cannot hold data for more than
+         one flight, and so must be erased after each flight or it
+         will not capture data for subsequent flights.
+       </para>
+      </section>
+      <section>
+       <title>Ignite Mode</title>
+       <para>
+         Instead of firing one charge at apogee and another charge at
+         a fixed height above the ground, you can configure the
+         altimeter to fire both at apogee or both during
+         descent. This was added to support an airframe that has two
+         TeleMetrum computers, one in the fin can and one in the
+         nose.
+       </para>
+       <para>
+         Providing the ability to use both igniters for apogee or
+         main allows some level of redundancy without needing two
+         flight computers.  In Redundant Apogee or Redundant Main
+         mode, the two charges will be fired two seconds apart.
+       </para>
+      </section>
+      <section>
+       <title>Pad Orientation</title>
+       <para>
+         TeleMetrum measures acceleration along the axis of the
+         board. Which way the board is oriented affects the sign of
+         the acceleration value. Instead of trying to guess which way
+         the board is mounted in the air frame, TeleMetrum must be
+         explicitly configured for either Antenna Up or Antenna
+         Down. The default, Antenna Up, expects the end of the
+         TeleMetrum board connected to the 70cm antenna to be nearest
+         the nose of the rocket, with the end containing the screw
+         terminals nearest the tail.
+       </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, TeleMini and TeleDongle. AltosUI can monitor telemetry data,
+      configure TeleMetrum, TeleMini 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>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 frequency being monitored by the TeleDongle device is
+        displayed at the top of the window. You can configure the
+        frequency by clicking on the frequency box and selecting the desired
+        frequency. AltosUI remembers the last frequency selected for each
+        TeleDongle and selects that automatically the next time you use
+        that device.
+      </para>
+      <para>
+        Below the TeleDongle frequency selector, the window contains a few
+        significant pieces of information about the altimeter providing
+        the telemetry data stream:
+      </para>
+      <itemizedlist>
+        <listitem>
+          <para>The configured call-sign</para>
+        </listitem>
+        <listitem>
+          <para>The device serial number</para>
+        </listitem>
+        <listitem>
+          <para>The flight number. Each altimeter 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 receivable. The packet link uses
+            error detection and correction 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 displays all of
+        the raw telemetry values in one place in a spreadsheet-like format.
+      </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 Li-Po 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 Li-Po 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 Li-Po battery voltage. A value greater than 3.2V is
+                required for a 'GO' status.
+              </para>
+            </listitem>
+           <listitem>
+             <para>
+               On-board Data Logging. This indicates whether there is
+               space remaining on-board to store flight data for the
+               upcoming flight. If you've downloaded data, but failed
+               to erase flights, there may not be any space
+               left. TeleMetrum can store multiple flights, depending
+               on the configured maximum flight log size. TeleMini
+               stores only a single flight, so it will need to be
+               downloaded and erased after each flight to capture
+               data. This only affects on-board flight logging; the
+               altimeter will still transmit telemetry and fire
+               ejection charges at the proper times.
+             </para>
+           </listitem>
+            <listitem>
+              <para>
+                GPS Locked. For a TeleMetrum device, 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. For a TeleMetrum device, 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
+          maximum 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 TeleMetrum 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 vary based on the choice of recovery
+         components, but generally range from 15-30m/s on drogue and should
+         be below 10m/s when under the main parachute in a dual-deploy flight.
+        </para>
+        <para>
+          For TeleMetrum altimeters, you can locate the rocket in the sky
+         using 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.  Note that some commercial
+         e-matches are designed to retain continuity even after being
+         fired, and will continue to show as green or return from red to
+         green after firing.
+        </para>
+      </section>
+      <section>
+        <title>Landed</title>
+        <para>
+          Once the rocket is on the ground, attention switches to
+          recovery. While the radio signal is often 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 to walk or hitch a ride.  Take the reported
+          latitude and longitude and enter them into your hand-held GPS
+          unit and have that compute a track to the landing location.
+        </para>
+       <para>
+         Both TeleMini and TeleMetrum will continue to transmit RDF
+         tones after landing, allowing you to locate the rocket by
+         following the radio signal if necessary. You may need to get 
+         away from the clutter of the flight line, or even get up on 
+         a hill (or your neighbor's RV roof) to receive the RDF signal.
+       </para>
+        <para>
+          The maximum height, speed and acceleration reported
+          during the flight are displayed for your admiring observers.
+         The accuracy of these immediate values depends on the quality
+         of your radio link and how many packets were received.  
+         Recovering the on-board data after flight will likely yield
+         more precise results.
+        </para>
+       <para>
+         To get more detailed information about the flight, you can
+         click on the 'Graph Flight' button which will bring up a
+         graph window for the current flight.
+       </para>
+      </section>
+      <section>
+        <title>Site Map</title>
+        <para>
+          When the TeleMetrum has 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 color: 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 centered while data is being received.
+        </para>
+        <para>
+          Images are fetched automatically via the Google Maps Static API,
+          and cached on disk for reuse. If map images cannot be downloaded,
+          the rocket's path will be traced on a dark gray background
+          instead.
+        </para>
+       <para>
+         You can pre-load images for your favorite launch sites
+         before you leave home; check out the 'Preload Maps' section below.
+       </para>
+      </section>
+    </section>
+    <section>
+      <title>Save Flight Data</title>
+      <para>
+        The altimeter records flight data to its internal flash memory.
+        TeleMetrum 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. As TeleMini has only a barometer, it
+       records data at the same rate as the telemetry signal, but there will be
+       no data lost due to telemetry drop-outs.
+      </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 an altimeter over radio link via the 
+       specified TeleDongle. See the chapter on Controlling An Altimeter 
+       Over The Radio Link for more information.
+      </para>
+      <para>
+       After the device has been selected, a dialog showing the
+       flight data saved in the device will be shown allowing you to
+       select which flights to download and which to delete. With
+       version 0.9 or newer firmware, you must erase flights in order
+       for the space they consume to be reused by another
+       flight. This prevents accidentally losing flight data
+       if you neglect to download data before flying again. Note that
+       if there is no more space available in the device, then no
+       data will be recorded during the next flight.
+      </para>
+      <para>
+        The file name for each flight log is computed automatically
+        from the recorded flight date, altimeter 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 altimeter
+        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
+        flash memory.
+      </para>
+      <para>
+        Once a flight record is selected, a window with two tabs is
+        opened. The first tab contains a graph with acceleration
+        (blue), velocity (green) and altitude (red) of the flight,
+        measured in metric units. The
+        apogee(yellow) and main(magenta) igniter voltages are also
+        displayed; high voltages indicate continuity, low voltages
+        indicate open circuits. The second tab contains some basic
+       flight statistics.
+      </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 pop-up 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.
+        Use saved flight data in .eeprom files 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 altimeter, then
+          there is a single header line which labels all of the
+          fields. All of these lines start with a '#' character which
+          many 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 Google Earth to provide an overlay 
+         within that application. With this, you can use Google Earth to 
+         see the whole flight path in 3D.
+        </para>
+      </section>
+    </section>
+    <section>
+      <title>Configure Altimeter</title>
+      <para>
+        Select this button and then select either a TeleMetrum or
+        TeleDongle Device from the list provided. Selecting a TeleDongle
+        device will use the radio link to configure a remote altimeter. 
+      </para>
+      <para>
+        The first few lines of the dialog provide information about the
+        connected 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
+            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 device. Use this to
+            switch from idle to pad mode by rebooting once the rocket is
+            oriented for flight, or to confirm changes you think you saved 
+           are really saved.
+          </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 over pressurize the apogee deployment
+          bay and cause a structural failure of the air-frame. 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 Frequency</title>
+        <para>
+          This configures which of the configured frequencies 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 frequency 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.  If you need to you can adjust the calibration 
+         by changing this value.  Do not do this without understanding what
+         the value means, read the appendix on calibration and/or the source
+         code for more information.  To change a TeleDongle's calibration, 
+         you must reprogram the unit completely.
+        </para>
+      </section>
+      <section>
+        <title>Callsign</title>
+        <para>
+          This sets the call sign included in each telemetry packet. Set this
+          as needed to conform to your local radio regulations.
+        </para>
+      </section>
+      <section>
+        <title>Maximum Flight Log Size</title>
+        <para>
+          This sets the space (in kilobytes) allocated for each flight
+          log. The available space will be divided into chunks of this
+          size. A smaller value will allow more flights to be stored,
+          a larger value will record data from longer flights.
+       </para>
+      </section>
+      <section>
+        <title>Ignite Mode</title>
+       <para>
+         TeleMetrum and TeleMini provide two igniter channels as they
+         were originally designed as dual-deploy flight
+         computers. This configuration parameter allows the two
+         channels to be used in different configurations.
+       </para>
+       <itemizedlist>
+         <listitem>
+           <para>
+             Dual Deploy. This is the usual mode of operation; the
+             'apogee' channel is fired at apogee and the 'main'
+             channel at the height above ground specified by the
+             'Main Deploy Altitude' during descent.
+           </para>
+         </listitem>
+         <listitem>
+           <para>
+             Redundant Apogee. This fires both channels at
+             apogee, the 'apogee' channel first followed after a two second
+             delay by the 'main' channel.
+           </para>
+         </listitem>
+         <listitem>
+           <para>
+             Redundant Main. This fires both channels at the
+             height above ground specified by the Main Deploy
+             Altitude setting during descent. The 'apogee'
+             channel is fired first, followed after a two second
+             delay by the 'main' channel.
+           </para>
+         </listitem>
+       </itemizedlist>
+      </section>
+      <section>
+        <title>Pad Orientation</title>
+       <para>
+         Because it includes an accelerometer, TeleMetrum is
+         sensitive to the orientation of the board. By default, it
+         expects the antenna end to point forward. This parameter
+         allows that default to be changed, permitting the board to
+         be mounted with the antenna pointing aft instead.
+       </para>
+       <itemizedlist>
+         <listitem>
+           <para>
+             Antenna Up. In this mode, the antenna end of the
+             TeleMetrum board must point forward, in line with the
+             expected flight path.
+           </para>
+         </listitem>
+         <listitem>
+           <para>
+             Antenna Down. In this mode, the antenna end of the
+             TeleMetrum board must point aft, in line with the
+             expected flight path.
+           </para>
+         </listitem>
+       </itemizedlist>
+      </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 announcements 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 system 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 transmitted in each command packet sent from 
+         TeleDongle and received from an altimeter.  It is not used in 
+         telemetry mode, as the callsign configured in the altimeter board
+         is included in all telemetry packets.  Configure this
+          with the AltosUI operators call sign as needed to comply with
+          your local radio regulations.
+        </para>
+      </section>
+      <section>
+       <title>Font Size</title>
+       <para>
+         Selects the set of fonts used in the flight monitor
+         window. Choose between the small, medium and large sets.
+       </para>
+      </section>
+      <section>
+        <title>Serial Debug</title>
+        <para>
+          This causes all communication with a connected device to be
+          dumped to the console from which AltosUI was started. If
+          you've started it from an icon or menu entry, the output
+          will simply be discarded. This mode can be useful to debug
+          various serial communication issues.
+        </para>
+      </section>
+      <section>
+       <title>Manage Frequencies</title>
+       <para>
+         This brings up a dialog where you can configure the set of
+         frequencies shown in the various frequency menus. You can
+         add as many as you like, or even reconfigure the default
+         set. Changing this list does not affect the frequency
+         settings of any devices, it only changes the set of
+         frequencies shown in the menus.
+       </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 flashing devices in the Updating Device
+        Firmware chapter below.
+      </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>
+       This activates the igniter circuits in TeleMetrum to help test
+       recovery systems deployment. Because this command can operate
+       over the Packet Command Link, you can prepare the rocket as
+       for flight and then test the recovery system without needing
+       to snake wires inside the air-frame.
+      </para>
+      <para>
+       Selecting the 'Fire Igniter' button brings up the usual device
+       selection dialog. Pick the desired TeleDongle or TeleMetrum
+       device. This brings up another window which shows the current
+       continuity test status for both apogee and main charges.
+      </para>
+      <para>
+       Next, select the desired igniter to fire. This will enable the
+       'Arm' button.
+      </para>
+      <para>
+       Select the 'Arm' button. This enables the 'Fire' button. The
+       word 'Arm' is replaced by a countdown timer indicating that
+       you have 10 seconds to press the 'Fire' button or the system
+       will deactivate, at which point you start over again at
+       selecting the desired igniter.
+      </para>
+    </section>
+    <section>
+      <title>Scan Channels</title>
+      <para>
+       This listens for telemetry packets on all of the configured
+       frequencies, displaying information about each device it
+       receives a packet from. You can select which of the three
+       telemetry formats should be tried; by default, it only listens
+       for the standard telemetry packets used in v1.0 and later
+       firmware.
+      </para>
+    </section>
+    <section>
+      <title>Load Maps</title>
+      <para>
+       Before heading out to a new launch site, you can use this to
+       load satellite images in case you don't have internet
+       connectivity at the site. This loads a fairly large area
+       around the launch site, which should cover any flight you're likely to make.
+      </para>
+      <para>
+       There's a drop-down menu of launch sites we know about; if
+       your favorites aren't there, please let us know the lat/lon
+       and name of the site. The contents of this list are actually
+       downloaded at run-time, so as new sites are sent in, they'll
+       get automatically added to this list.
+      </para>
+      <para>
+       If the launch site isn't in the list, you can manually enter the lat/lon values
+      </para>
+      <para>
+       Clicking the 'Load Map' button will fetch images from Google
+       Maps; note that Google limits how many images you can fetch at
+       once, so if you load more than one launch site, you may get
+       some gray areas in the map which indicate that Google is tired
+       of sending data to you. Try again later.
+      </para>
+    </section>
+    <section>
+      <title>Monitor Idle</title>
+      <para>
+       This brings up a dialog similar to the Monitor Flight UI,
+       except it works with the altimeter in "idle" mode by sending
+       query commands to discover the current state rather than
+       listening for telemetry packets.
+      </para>
+    </section>
+  </chapter>
+  <chapter>
+    <title>Using Altus Metrum Products</title>
+    <section>
+      <title>Being Legal</title>
+      <para>
+        First off, in the US, you need an <ulink url="http://www.altusmetrum.org/Radio/">amateur radio license</ulink> or
+        other authorization to legally operate the radio transmitters that are part
+        of our products.
+      </para>
+      </section>
+      <section>
+        <title>In the Rocket</title>
+        <para>
+          In the rocket itself, you just need a <ulink url="http://www.altusmetrum.org/TeleMetrum/">TeleMetrum</ulink> or
+         <ulink url="http://www.altusmetrum.org/TeleMini/">TeleMini</ulink> board and
+          a single-cell, 3.7 volt nominal Li-Po rechargeable battery.  An 
+         850mAh battery weighs less than a 9V alkaline battery, and will 
+         run a TeleMetrum for hours.
+         A 110mAh battery weighs less than a triple A battery and will run a TeleMetrum for
+         a few hours, or a TeleMini for much (much) longer.
+        </para>
+        <para>
+          By default, we ship the altimeters with a simple wire antenna.  If your
+          electronics bay or the air-frame it resides within is made of carbon fiber,
+          which is opaque to RF signals, you may choose to have an SMA connector
+          installed so that you can run a coaxial cable to an antenna mounted
+          elsewhere in the rocket.
+        </para>
+      </section>
+      <section>
+        <title>On the Ground</title>
+        <para>
+          To receive the data stream from the rocket, you need an antenna and short
+          feed-line connected to one of our <ulink url="http://www.altusmetrum.org/TeleDongle/">TeleDongle</ulink> units.  The
+          TeleDongle in turn plugs directly into the USB port on a notebook
+          computer.  Because TeleDongle looks like a simple serial port, your computer
+          does not require special device drivers... just plug it in.
+        </para>
+        <para>
+         The GUI tool, AltosUI, is written in Java and runs across
+         Linux, Mac OS and Windows. There's also a suite of C tools
+         for Linux which can perform most of the same tasks.
+        </para>
+        <para>
+          After the flight, you can use the radio link to extract the more detailed data
+          logged in either TeleMetrum or TeleMini devices, or you can use a mini USB cable to plug into the
+          TeleMetrum board directly.  Pulling out the data without having to open up
+          the rocket is pretty cool!  A USB cable is also how you charge the Li-Po
+          battery, so you'll want one of those anyway... the same cable used by lots
+          of digital cameras and other modern electronic stuff will work fine.
+        </para>
+        <para>
+          If your TeleMetrum-equipped rocket lands out of sight, you may enjoy having a hand-held GPS
+          receiver, so that you can put in a way-point for the last reported rocket
+          position before touch-down.  This makes looking for your rocket a lot like
+          Geo-Caching... just go to the way-point and look around starting from there.
+        </para>
+        <para>
+          You may also enjoy having a ham radio "HT" that covers the 70cm band... you
+          can use that with your antenna to direction-find the rocket on the ground
+          the same way you can use a Walston or Beeline tracker.  This can be handy
+          if the rocket is hiding in sage brush or a tree, or if the last GPS position
+          doesn't get you close enough because the rocket dropped into a canyon, or
+          the wind is blowing it across a dry lake bed, or something like that...  Keith
+          and Bdale both currently own and use the Yaesu VX-7R at launches.
+        </para>
+        <para>
+          So, to recap, on the ground the hardware you'll need includes:
+          <orderedlist inheritnum='inherit' numeration='arabic'>
+            <listitem>
+              an antenna and feed-line
+            </listitem>
+            <listitem>
+              a TeleDongle
+            </listitem>
+            <listitem>
+              a notebook computer
+            </listitem>
+            <listitem>
+              optionally, a hand-held GPS receiver
+            </listitem>
+            <listitem>
+              optionally, an HT or receiver covering 435 MHz
+            </listitem>
+          </orderedlist>
+        </para>
+        <para>
+          The best hand-held commercial directional antennas we've found for radio
+          direction finding rockets are from
+          <ulink url="http://www.arrowantennas.com/" >
+            Arrow Antennas.
+          </ulink>
+          The 440-3 and 440-5 are both good choices for finding a
+          TeleMetrum- or TeleMini- equipped rocket when used with a suitable 70cm HT.
+        </para>
+      </section>
+      <section>
+        <title>Data Analysis</title>
+        <para>
+          Our software makes it easy to log the data from each flight, both the
+          telemetry received during the flight itself, and the more
+          complete data log recorded in the flash memory on the altimeter
+          board.  Once this data is on your computer, our post-flight tools make it
+          easy to quickly get to the numbers everyone wants, like apogee altitude,
+          max acceleration, and max velocity.  You can also generate and view a
+          standard set of plots showing the altitude, acceleration, and
+          velocity of the rocket during flight.  And you can even export a TeleMetrum data file
+          usable with Google Maps and Google Earth for visualizing the flight path
+          in two or three dimensions!
+        </para>
+        <para>
+          Our ultimate goal is to emit a set of files for each flight that can be
+          published as a web page per flight, or just viewed on your local disk with
+          a web browser.
+        </para>
+      </section>
+      <section>
+        <title>Future Plans</title>
+        <para>
+          In the future, we intend to offer "companion boards" for the rocket that will
+          plug in to TeleMetrum to collect additional data, provide more pyro channels,
+          and so forth.  
+        </para>
+        <para>
+          We are also working on the design of a hand-held ground terminal that will
+          allow monitoring the rocket's status, collecting data during flight, and
+          logging data after flight without the need for a notebook computer on the
+          flight line.  Particularly since it is so difficult to read most notebook
+          screens in direct sunlight, we think this will be a great thing to have.
+        </para>
+        <para>
+          Because all of our work is open, both the hardware designs and the software,
+          if you have some great idea for an addition to the current Altus Metrum family,
+          feel free to dive in and help!  Or let us know what you'd like to see that
+          we aren't already working on, and maybe we'll get excited about it too...
+        </para>
+    </section>
+  </chapter>
+  <chapter>
+    <title>Altimeter Installation Recommendations</title>
+    <para>
+      Building high-power rockets that fly safely is hard enough. Mix
+      in some sophisticated electronics and a bunch of radio energy
+      and oftentimes you find few perfect solutions. This chapter
+      contains some suggestions about how to install Altus Metrum
+      products into the rocket air-frame, including how to safely and
+      reliably mix a variety of electronics into the same air-frame.
+    </para>
+    <section>
+      <title>Mounting the Altimeter</title>
+      <para>
+       The first consideration is to ensure that the altimeter is
+       securely fastened to the air-frame. For TeleMetrum, we use
+       nylon standoffs and nylon screws; they're good to at least 50G
+       and cannot cause any electrical issues on the board. For
+       TeleMini, we usually cut small pieces of 1/16" balsa to fit
+       under the screw holes, and then take 2x56 nylon screws and
+       screw them through the TeleMini mounting holes, through the
+       balsa and into the underlying material.
+      </para>
+      <orderedlist inheritnum='inherit' numeration='arabic'>
+       <listitem>
+         Make sure TeleMetrum is aligned precisely along the axis of
+         acceleration so that the accelerometer can accurately
+         capture data during the flight.
+       </listitem>
+       <listitem>
+         Watch for any metal touching components on the
+         board. Shorting out connections on the bottom of the board
+         can cause the altimeter to fail during flight.
+       </listitem>
+      </orderedlist>
+    </section>
+    <section>
+      <title>Dealing with the Antenna</title>
+      <para>
+       The antenna supplied is just a piece of solid, insulated,
+       wire. If it gets damaged or broken, it can be easily
+       replaced. It should be kept straight and not cut; bending or
+       cutting it will change the resonant frequency and/or
+       impedance, making it a less efficient radiator and thus
+       reducing the range of the telemetry signal.
+      </para>
+      <para>
+       Keeping metal away from the antenna will provide better range
+       and a more even radiation pattern. In most rockets, it's not
+       entirely possible to isolate the antenna from metal
+       components; there are often bolts, all-thread and wires from other
+       electronics to contend with. Just be aware that the more stuff
+       like this around the antenna, the lower the range.
+      </para>
+      <para>
+       Make sure the antenna is not inside a tube made or covered
+       with conducting material. Carbon fiber is the most common
+       culprit here -- CF is a good conductor and will effectively
+       shield the antenna, dramatically reducing signal strength and
+       range. Metallic flake paint is another effective shielding
+       material which is to be avoided around any antennas.
+      </para>
+      <para>
+       If the ebay is large enough, it can be convenient to simply
+       mount the altimeter at one end and stretch the antenna out
+       inside. Taping the antenna to the sled can keep it straight
+       under acceleration. If there are metal rods, keep the
+       antenna as far away as possible.
+      </para>
+      <para>
+       For a shorter ebay, it's quite practical to have the antenna
+       run through a bulkhead and into an adjacent bay. Drill a small
+       hole in the bulkhead, pass the antenna wire through it and
+       then seal it up with glue or clay. We've also used acrylic
+       tubing to create a cavity for the antenna wire. This works a
+       bit better in that the antenna is known to stay straight and
+       not get folded by recovery components in the bay. Angle the
+       tubing towards the side wall of the rocket and it ends up
+       consuming very little space.
+      </para>
+      <para>
+       If you need to place the antenna at a distance from the
+       altimeter, you can replace the antenna with an edge-mounted
+       SMA connector, and then run 50Ω coax from the board to the
+       antenna. Building a remote antenna is beyond the scope of this
+       manual.
+      </para>
+    </section>
+    <section>
+      <title>Preserving GPS Reception</title>
+      <para>
+       The GPS antenna and receiver in TeleMetrum are highly
+       sensitive and normally have no trouble tracking enough
+       satellites to provide accurate position information for
+       recovering the rocket. However, there are many ways to
+       attenuate the GPS signal.
+      <orderedlist inheritnum='inherit' numeration='arabic'>
+       <listitem>
+         Conductive tubing or coatings. Carbon fiber and metal
+         tubing, or metallic paint will all dramatically attenuate the
+         GPS signal. We've never heard of anyone successfully
+         receiving GPS from inside these materials.
+       </listitem>
+       <listitem>
+         Metal components near the GPS patch antenna. These will
+         de-tune the patch antenna, changing the resonant frequency
+         away from the L1 carrier and reduce the effectiveness of the
+         antenna. You can place as much stuff as you like beneath the
+         antenna as that's covered with a ground plane. But, keep
+         wires and metal out from above the patch antenna.
+       </listitem>
+      </orderedlist>
+      </para>
+    </section>
+    <section>
+      <title>Radio Frequency Interference</title>
+      <para>
+       Any altimeter will generate RFI; the digital circuits use
+       high-frequency clocks that spray radio interference across a
+       wide band. Altus Metrum altimeters generate intentional radio
+       signals as well, increasing the amount of RF energy around the board.
+      </para>
+      <para>
+       Rocketry altimeters also use precise sensors measuring air
+       pressure and acceleration. Tiny changes in voltage can cause
+       these sensor readings to vary by a huge amount. When the
+       sensors start mis-reporting data, the altimeter can either
+       fire the igniters at the wrong time, or not fire them at all.
+      </para>
+      <para>
+       Voltages are induced when radio frequency energy is
+       transmitted from one circuit to another. Here are things that
+       influence the induced voltage and current:
+      </para>
+      <itemizedlist>
+       <listitem>
+         Keep wires from different circuits apart. Moving circuits
+         further apart will reduce RFI.
+       </listitem>
+       <listitem>
+         Avoid parallel wires from different circuits. The longer two
+         wires run parallel to one another, the larger the amount of
+         transferred energy. Cross wires at right angles to reduce
+         RFI.
+       </listitem>
+       <listitem>
+         Twist wires from the same circuits. Two wires the same
+         distance from the transmitter will get the same amount of
+         induced energy which will then cancel out. Any time you have
+         a wire pair running together, twist the pair together to
+         even out distances and reduce RFI. For altimeters, this
+         includes battery leads, switch hookups and igniter
+         circuits.
+       </listitem>
+       <listitem>
+         Avoid resonant lengths. Know what frequencies are present
+         in the environment and avoid having wire lengths near a
+         natural resonant length. Altusmetrum products transmit on the
+         70cm amateur band, so you should avoid lengths that are a
+         simple ratio of that length; essentially any multiple of 1/4
+         of the wavelength (17.5cm).
+       </listitem>
+      </itemizedlist>
+    </section>
+    <section>
+      <title>The Barometric Sensor</title>
+      <para>
+       Altusmetrum altimeters measure altitude with a barometric
+       sensor, essentially measuring the amount of air above the
+       rocket to figure out how high it is. A large number of
+       measurements are taken as the altimeter initializes itself to
+       figure out the pad altitude. Subsequent measurements are then
+       used to compute the height above the pad.
+      </para>
+      <para>
+       To accurately measure atmospheric pressure, the ebay
+       containing the altimeter must be vented outside the
+       air-frame. The vent must be placed in a region of linear
+       airflow, have smooth edges, and away from areas of increasing or 
+       decreasing pressure.
+      </para>
+      <para>
+       The barometric sensor in the altimeter is quite sensitive to
+       chemical damage from the products of APCP or BP combustion, so
+       make sure the ebay is carefully sealed from any compartment
+       which contains ejection charges or motors.
+      </para>
+    </section>
+    <section>
+      <title>Ground Testing</title>
+      <para>
+       The most important aspect of any installation is careful
+       ground testing. Bringing an air-frame up to the LCO table which
+       hasn't been ground tested can lead to delays or ejection
+       charges firing on the pad, or, even worse, a recovery system
+       failure.
+      </para>
+      <para>
+       Do a 'full systems' test that includes wiring up all igniters
+       without any BP and turning on all of the electronics in flight
+       mode. This will catch any mistakes in wiring and any residual
+       RFI issues that might accidentally fire igniters at the wrong
+       time. Let the air-frame sit for several minutes, checking for
+       adequate telemetry signal strength and GPS lock.  If any igniters
+       fire unexpectedly, find and resolve the issue before loading any
+       BP charges!
+      </para>
+      <para>
+       Ground test the ejection charges. Prepare the rocket for
+       flight, loading ejection charges and igniters. Completely
+       assemble the air-frame and then use the 'Fire Igniters'
+       interface through a TeleDongle to command each charge to
+       fire. Make sure the charge is sufficient to robustly separate
+       the air-frame and deploy the recovery system.
+      </para>
+    </section>
+  </chapter>
+  <chapter>
+    <title>Updating Device Firmware</title>
+    <para>
+      The big concept to understand is that you have to use a
+      TeleDongle as a programmer to update a TeleMetrum or TeleMini,
+      and a TeleMetrum or other TeleDongle to program the TeleDongle
+      Due to limited memory resources in the cc1111, we don't support
+      programming directly over USB. 
+    </para>
+    <para>
+      You may wish to begin by ensuring you have current firmware images.
+      These are distributed as part of the AltOS software bundle that
+      also includes the AltosUI ground station program.  Newer ground
+      station versions typically work fine with older firmware versions,
+      so you don't need to update your devices just to try out new
+      software features.  You can always download the most recent
+      version from <ulink url="http://www.altusmetrum.org/AltOS/"/>.
+    </para>
+    <para>
+      We recommend updating the altimeter first, before updating TeleDongle.
+    </para>
+    <section>
+      <title>Updating TeleMetrum Firmware</title>
+      <orderedlist inheritnum='inherit' numeration='arabic'>
+        <listitem>
+          Find the 'programming cable' that you got as part of the starter
+          kit, that has a red 8-pin MicroMaTch connector on one end and a
+          red 4-pin MicroMaTch connector on the other end.
+        </listitem>
+        <listitem>
+          Take the 2 screws out of the TeleDongle case to get access
+          to the circuit board.
+        </listitem>
+        <listitem>
+          Plug the 8-pin end of the programming cable to the
+          matching connector on the TeleDongle, and the 4-pin end to the
+          matching connector on the TeleMetrum.
+         Note that each MicroMaTch connector has an alignment pin that
+         goes through a hole in the PC board when you have the cable
+         oriented correctly.
+        </listitem>
+        <listitem>
+          Attach a battery to the TeleMetrum board.
+        </listitem>
+        <listitem>
+          Plug the TeleDongle into your computer's USB port, and power
+          up the TeleMetrum.
+        </listitem>
+        <listitem>
+          Run AltosUI, and select 'Flash Image' from the File menu.
+        </listitem>
+        <listitem>
+          Pick the TeleDongle device from the list, identifying it as the
+          programming device.
+        </listitem>
+        <listitem>
+          Select the image you want put on the TeleMetrum, which should have a
+          name in the form telemetrum-v1.2-1.0.0.ihx.  It should be visible
+       in the default directory, if not you may have to poke around
+       your system to find it.
+        </listitem>
+        <listitem>
+          Make sure the configuration parameters are reasonable
+          looking. If the serial number and/or RF configuration
+          values aren't right, you'll need to change them.
+        </listitem>
+        <listitem>
+          Hit the 'OK' button and the software should proceed to flash
+          the TeleMetrum with new firmware, showing a progress bar.
+        </listitem>
+        <listitem>
+          Confirm that the TeleMetrum board seems to have updated OK, which you
+          can do by plugging in to it over USB and using a terminal program
+          to connect to the board and issue the 'v' command to check
+          the version, etc.
+        </listitem>
+        <listitem>
+          If something goes wrong, give it another try.
+        </listitem>
+      </orderedlist>
+    </section>
+    <section>
+      <title>Updating TeleMini Firmware</title>
+      <orderedlist inheritnum='inherit' numeration='arabic'>
+        <listitem>
+         You'll need a special 'programming cable' to reprogram the
+         TeleMini. It's available on the Altus Metrum web store, or
+         you can make your own using an 8-pin MicroMaTch connector on
+         one end and a set of four pins on the other.
+        </listitem>
+        <listitem>
+          Take the 2 screws out of the TeleDongle case to get access
+          to the circuit board.
+        </listitem>
+        <listitem>
+          Plug the 8-pin end of the programming cable to the matching
+          connector on the TeleDongle, and the 4-pins into the holes
+          in the TeleMini circuit board.  Note that the MicroMaTch
+          connector has an alignment pin that goes through a hole in
+          the PC board when you have the cable oriented correctly, and
+          that pin 1 on the TeleMini board is marked with a square pad
+          while the other pins have round pads.
+        </listitem>
+        <listitem>
+          Attach a battery to the TeleMini board.
+        </listitem>
+        <listitem>
+          Plug the TeleDongle into your computer's USB port, and power
+          up the TeleMini
+        </listitem>
+        <listitem>
+          Run AltosUI, and select 'Flash Image' from the File menu.
+        </listitem>
+        <listitem>
+          Pick the TeleDongle device from the list, identifying it as the
+          programming device.
+        </listitem>
+        <listitem>
+          Select the image you want put on the TeleMini, which should have a
+          name in the form telemini-v1.0-1.0.0.ihx.  It should be visible
+       in the default directory, if not you may have to poke around
+       your system to find it.
+        </listitem>
+        <listitem>
+          Make sure the configuration parameters are reasonable
+          looking. If the serial number and/or RF configuration
+          values aren't right, you'll need to change them.
+        </listitem>
+        <listitem>
+          Hit the 'OK' button and the software should proceed to flash
+          the TeleMini with new firmware, showing a progress bar.
+        </listitem>
+        <listitem>
+          Confirm that the TeleMini board seems to have updated OK, which you
+          can do by configuring it over the radio link through the TeleDongle, or
+         letting it come up in "flight" mode and listening for telemetry.
+        </listitem>
+        <listitem>
+          If something goes wrong, give it another try.
+        </listitem>
+      </orderedlist>
+    </section>
+    <section>
+      <title>Updating TeleDongle Firmware</title>
+      <para>
+        Updating TeleDongle's firmware is just like updating TeleMetrum or TeleMini
+       firmware, but you use either a TeleMetrum or another TeleDongle as the programmer.
+       </para>
+      <orderedlist inheritnum='inherit' numeration='arabic'>
+        <listitem>
+          Find the 'programming cable' that you got as part of the starter
+          kit, that has a red 8-pin MicroMaTch connector on one end and a
+          red 4-pin MicroMaTch connector on the other end.
+        </listitem>
+        <listitem>
+         Find the USB cable that you got as part of the starter kit, and
+         plug the "mini" end in to the mating connector on TeleMetrum or TeleDongle.
+        </listitem>
+        <listitem>
+          Take the 2 screws out of the TeleDongle case to get access
+          to the circuit board.
+        </listitem>
+        <listitem>
+          Plug the 8-pin end of the programming cable to the
+          matching connector on the programmer, and the 4-pin end to the
+          matching connector on the TeleDongle.
+         Note that each MicroMaTch connector has an alignment pin that
+         goes through a hole in the PC board when you have the cable
+         oriented correctly.
+        </listitem>
+        <listitem>
+          Attach a battery to the TeleMetrum board if you're using one.
+        </listitem>
+        <listitem>
+          Plug both the programmer and the TeleDongle into your computer's USB
+         ports, and power up the programmer.
+        </listitem>
+        <listitem>
+          Run AltosUI, and select 'Flash Image' from the File menu.
+        </listitem>
+        <listitem>
+          Pick the programmer device from the list, identifying it as the
+          programming device.
+        </listitem>
+        <listitem>
+          Select the image you want put on the TeleDongle, which should have a
+          name in the form teledongle-v0.2-1.0.0.ihx.  It should be visible
+       in the default directory, if not you may have to poke around
+       your system to find it.
+        </listitem>
+        <listitem>
+          Make sure the configuration parameters are reasonable
+          looking. If the serial number and/or RF configuration
+          values aren't right, you'll need to change them.  The TeleDongle
+         serial number is on the "bottom" of the circuit board, and can
+         usually be read through the translucent blue plastic case without
+         needing to remove the board from the case.
+        </listitem>
+        <listitem>
+          Hit the 'OK' button and the software should proceed to flash
+          the TeleDongle with new firmware, showing a progress bar.
+        </listitem>
+        <listitem>
+          Confirm that the TeleDongle board seems to have updated OK, which you
+          can do by plugging in to it over USB and using a terminal program
+          to connect to the board and issue the 'v' command to check
+          the version, etc.  Once you're happy, remove the programming cable
+         and put the cover back on the TeleDongle.
+        </listitem>
+        <listitem>
+          If something goes wrong, give it another try.
+        </listitem>
+      </orderedlist>
+      <para>
+        Be careful removing the programming cable from the locking 8-pin
+        connector on TeleMetrum.  You'll need a fingernail or perhaps a thin
+        screwdriver or knife blade to gently pry the locking ears out
+        slightly to extract the connector.  We used a locking connector on
+        TeleMetrum to help ensure that the cabling to companion boards
+        used in a rocket don't ever come loose accidentally in flight.
+      </para>
+    </section>
+  </chapter>
+  <chapter>
+    <title>Hardware Specifications</title>
+    <section>
+      <title>TeleMetrum Specifications</title>
+      <itemizedlist>
+       <listitem>
+         <para>
+           Recording altimeter for model rocketry.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Supports dual deployment (can fire 2 ejection charges).
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           70cm ham-band transceiver for telemetry down-link.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Barometric pressure sensor good to 45k feet MSL.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           1-axis high-g accelerometer for motor characterization, capable of
+           +/- 50g using default part.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           On-board, integrated GPS receiver with 5Hz update rate capability.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           On-board 1 megabyte non-volatile memory for flight data storage.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           USB interface for battery charging, configuration, and data recovery.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Fully integrated support for Li-Po rechargeable batteries.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Uses Li-Po to fire e-matches, can be modified to support 
+           optional separate pyro battery if needed.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           2.75 x 1 inch board designed to fit inside 29mm air-frame coupler tube.
+         </para>
+       </listitem>
+      </itemizedlist>
+    </section>
+    <section>
+      <title>TeleMini Specifications</title>
+      <itemizedlist>
+       <listitem>
+         <para>
+           Recording altimeter for model rocketry.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Supports dual deployment (can fire 2 ejection charges).
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           70cm ham-band transceiver for telemetry down-link.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Barometric pressure sensor good to 45k feet MSL.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           On-board 5 kilobyte non-volatile memory for flight data storage.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           RF interface for battery charging, configuration, and data recovery.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Support for Li-Po rechargeable batteries, using an external charger.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Uses Li-Po to fire e-matches, can be modified to support 
+           optional separate pyro battery if needed.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           1.5 x .5 inch board designed to fit inside 18mm air-frame coupler tube.
+         </para>
+       </listitem>
+      </itemizedlist>
+    </section>
+  </chapter>
+  <chapter>
+    <title>FAQ</title>
+      <para>
+        TeleMetrum seems to shut off when disconnected from the
+        computer.  Make sure the battery is adequately charged.  Remember the
+        unit will pull more power than the USB port can deliver before the
+        GPS enters "locked" mode.  The battery charges best when TeleMetrum
+        is turned off.
+      </para>
+      <para>
+        It's impossible to stop the TeleDongle when it's in "p" mode, I have
+        to unplug the USB cable?  Make sure you have tried to "escape out" of
+        this mode.  If this doesn't work the reboot procedure for the
+        TeleDongle *is* to simply unplug it. 'cu' however will retain it's
+        outgoing buffer IF your "escape out" ('~~') does not work.
+        At this point using either 'ao-view' (or possibly
+        'cutemon') instead of 'cu' will 'clear' the issue and allow renewed
+        communication.
+      </para>
+      <para>
+        The amber LED (on the TeleMetrum) lights up when both
+        battery and USB are connected. Does this mean it's charging?
+        Yes, the yellow LED indicates the charging at the 'regular' rate.
+        If the led is out but the unit is still plugged into a USB port,
+        then the battery is being charged at a 'trickle' rate.
+      </para>
+      <para>
+        There are no "dit-dah-dah-dit" sound or lights like the manual mentions?
+        That's the "pad" mode.  Weak batteries might be the problem.
+        It is also possible that the TeleMetrum is horizontal and the output
+        is instead a "dit-dit" meaning 'idle'. For TeleMini, it's possible that
+       it received a command packet which would have left it in "pad" mode.
+      </para>
+      <para>
+        How do I save flight data?
+        Live telemetry is written to file(s) whenever AltosUI is connected
+        to the TeleDongle.  The file area defaults to ~/TeleMetrum
+        but is easily changed using the menus in AltosUI. The files that
+        are written end in '.telem'. The after-flight
+        data-dumped files will end in .eeprom and represent continuous data
+        unlike the .telem files that are subject to losses
+        along the RF data path.
+        See the above instructions on what and how to save the eeprom stored
+        data after physically retrieving your altimeter.  Make sure to save
+        the on-board data after each flight; while the TeleMetrum can store
+       multiple flights, you never know when you'll lose the altimeter...
+      </para>
+  </chapter>
+  <appendix>
+    <title>Notes for Older Software</title>
+    <para>
+      <emphasis>
+      Before AltosUI was written, using Altus Metrum devices required
+      some finesse with the Linux command line. There was a limited
+      GUI tool, ao-view, which provided functionality similar to the
+      Monitor Flight window in AltosUI, but everything else was a
+      fairly 80's experience. This appendix includes documentation for
+      using that software.
+      </emphasis>
+    </para>
+    <para>
+      Both TeleMetrum and TeleDongle can be directly communicated
+      with using USB ports. The first thing you should try after getting
+      both units plugged into to your computer's USB port(s) is to run
+      'ao-list' from a terminal-window to see what port-device-name each
+      device has been assigned by the operating system.
+      You will need this information to access the devices via their
+      respective on-board firmware and data using other command line
+      programs in the AltOS software suite.
+    </para>
+    <para>
+      TeleMini can be communicated with through a TeleDongle device
+      over the radio link. When first booted, TeleMini listens for a
+      TeleDongle device and if it receives a packet, it goes into
+      'idle' mode. Otherwise, it goes into 'pad' mode and waits to be
+      launched. The easiest way to get it talking is to start the
+      communication link on the TeleDongle and the power up the
+      TeleMini board.
+    </para>
+    <para>
+      To access the device's firmware for configuration you need a terminal
+      program such as you would use to talk to a modem.  The software
+      authors prefer using the program 'cu' which comes from the UUCP package
+      on most Unix-like systems such as Linux.  An example command line for
+      cu might be 'cu -l /dev/ttyACM0', substituting the correct number
+      indicated from running the
+      ao-list program.  Another reasonable terminal program for Linux is
+      'cutecom'.  The default 'escape'
+      character used by CU (i.e. the character you use to
+      issue commands to cu itself instead of sending the command as input
+      to the connected device) is a '~'. You will need this for use in
+      only two different ways during normal operations. First is to exit
+      the program by sending a '~.' which is called a 'escape-disconnect'
+      and allows you to close-out from 'cu'. The
+      second use will be outlined later.
+    </para>
+    <para>
+      All of the Altus Metrum devices share the concept of a two level
+      command set in their firmware.
+      The first layer has several single letter commands. Once
+      you are using 'cu' (or 'cutecom') sending (typing) a '?'
+      returns a full list of these
+      commands. The second level are configuration sub-commands accessed
+      using the 'c' command, for
+      instance typing 'c?' will give you this second level of commands
+      (all of which require the
+      letter 'c' to access).  Please note that most configuration options
+      are stored only in Flash memory; TeleDongle doesn't provide any storage
+      for these options and so they'll all be lost when you unplug it.
+    </para>
+    <para>
+      Try setting these configuration ('c' or second level menu) values.  A good
+      place to start is by setting your call sign.  By default, the boards
+      use 'N0CALL' which is cute, but not exactly legal!
+      Spend a few minutes getting comfortable with the units, their
+      firmware, and 'cu' (or possibly 'cutecom').
+      For instance, try to send
+      (type) a 'c r 2' and verify the channel change by sending a 'c s'.
+      Verify you can connect and disconnect from the units while in your
+      terminal program by sending the escape-disconnect mentioned above.
+    </para>
+        <para>
+          To set the radio frequency, use the 'c R' command to specify the
+         radio transceiver configuration parameter. This parameter is computed
+         using the desired frequency, 'F', the radio calibration parameter, 'C' (showed by the 'c s' command) and
+         the standard calibration reference frequency, 'S', (normally 434.550MHz):
+         <programlisting>
+           R = F / S * C
+         </programlisting>
+         Round the result to the nearest integer value.
+          As with all 'c' sub-commands, follow this with a 'c w' to write the
+          change to the parameter block in the on-board flash on
+          your altimeter board if you want the change to stay in place across reboots.
+        </para>
+        <para>
+          To set the apogee delay, use the 'c d' command.
+          As with all 'c' sub-commands, follow this with a 'c w' to write the
+          change to the parameter block in the on-board DataFlash chip.
+        </para>
+        <para>
+          To set the main deployment altitude, use the 'c m' command.
+          As with all 'c' sub-commands, follow this with a 'c w' to write the
+          change to the parameter block in the on-board DataFlash chip.
+        </para>
+        <para>
+          To calibrate the radio frequency, connect the UHF antenna port to a
+          frequency counter, set the board to 434.550MHz, and use the 'C'
+          command to generate a CW carrier.  Wait for the transmitter temperature
+          to stabilize and the frequency to settle down.
+          Then, divide 434.550 MHz by the
+          measured frequency and multiply by the current radio cal value show
+          in the 'c s' command.  For an unprogrammed board, the default value
+          is 1186611.  Take the resulting integer and program it using the 'c f'
+          command.  Testing with the 'C' command again should show a carrier
+          within a few tens of Hertz of the intended frequency.
+          As with all 'c' sub-commands, follow this with a 'c w' to write the
+          change to the parameter block in the on-board DataFlash chip.
+        </para>
+    <para>
+      Note that the 'reboot' command, which is very useful on the altimeters,
+      will likely just cause problems with the dongle.  The *correct* way
+      to reset the dongle is just to unplug and re-plug it.
+    </para>
+    <para>
+      A fun thing to do at the launch site and something you can do while
+      learning how to use these units is to play with the radio link access
+      between an altimeter and the TeleDongle.  Be aware that you *must* create
+      some physical separation between the devices, otherwise the link will
+      not function due to signal overload in the receivers in each device.
+    </para>
+    <para>
+      Now might be a good time to take a break and read the rest of this
+      manual, particularly about the two "modes" that the altimeters
+      can be placed in. TeleMetrum uses the position of the device when booting
+      up will determine whether the unit is in "pad" or "idle" mode. TeleMini
+      enters "idle" mode when it receives a command packet within the first 5 seconds
+      of being powered up, otherwise it enters "pad" mode.
+    </para>
+    <para>
+      You can access an altimeter in idle mode from the TeleDongle's USB
+      connection using the radio link
+      by issuing a 'p' command to the TeleDongle. Practice connecting and
+      disconnecting ('~~' while using 'cu') from the altimeter.  If
+      you cannot escape out of the "p" command, (by using a '~~' when in
+      CU) then it is likely that your kernel has issues.  Try a newer version.
+    </para>
+    <para>
+      Using this radio link allows you to configure the altimeter, test
+      fire e-matches and igniters from the flight line, check pyro-match
+      continuity and so forth. You can leave the unit turned on while it
+      is in 'idle mode' and then place the
+      rocket vertically on the launch pad, walk away and then issue a
+      reboot command.  The altimeter will reboot and start sending data
+      having changed to the "pad" mode. If the TeleDongle is not receiving
+      this data, you can disconnect 'cu' from the TeleDongle using the
+      procedures mentioned above and THEN connect to the TeleDongle from
+      inside 'ao-view'. If this doesn't work, disconnect from the
+      TeleDongle, unplug it, and try again after plugging it back in.
+    </para>
+    <para>
+      In order to reduce the chance of accidental firing of pyrotechnic
+      charges, the command to fire a charge is intentionally somewhat
+      difficult to type, and the built-in help is slightly cryptic to
+      prevent accidental echoing of characters from the help text back at
+      the board from firing a charge.  The command to fire the apogee
+      drogue charge is 'i DoIt drogue' and the command to fire the main
+      charge is 'i DoIt main'.
+    </para>
+    <para>
+      On TeleMetrum, the GPS will eventually find enough satellites, lock in on them,
+      and 'ao-view' will both auditorily announce and visually indicate
+      that GPS is ready.
+      Now you can launch knowing that you have a good data path and
+      good satellite lock for flight data and recovery.  Remember
+      you MUST tell ao-view to connect to the TeleDongle explicitly in
+      order for ao-view to be able to receive data.
+    </para>
+    <para>
+      The altimeters provide RDF (radio direction finding) tones on
+      the pad, during descent and after landing. These can be used to
+      locate the rocket using a directional antenna; the signal
+      strength providing an indication of the direction from receiver to rocket.
+    </para>
+    <para>
+      TeleMetrum also provides GPS tracking data, which can further simplify
+      locating the rocket once it has landed. (The last good GPS data
+      received before touch-down will be on the data screen of 'ao-view'.)
+    </para>
+    <para>
+      Once you have recovered the rocket you can download the eeprom
+      contents using either 'ao-dumplog' (or possibly 'ao-eeprom'), over
+      either a USB cable or over the radio link using TeleDongle.
+      And by following the man page for 'ao-postflight' you can create
+      various data output reports, graphs, and even KML data to see the
+      flight trajectory in Google-earth. (Moving the viewing angle making
+      sure to connect the yellow lines while in Google-earth is the proper
+      technique.)
+    </para>
+    <para>
+      As for ao-view.... some things are in the menu but don't do anything
+      very useful.  The developers have stopped working on ao-view to focus
+      on a new, cross-platform ground station program.  So ao-view may or
+      may not be updated in the future.  Mostly you just use
+      the Log and Device menus.  It has a wonderful display of the incoming
+      flight data and I am sure you will enjoy what it has to say to you
+      once you enable the voice output!
+    </para>
+  </appendix>
+  <appendix>
+      <title>Calibration</title>
+      <para>
+        There are only two calibrations required for a TeleMetrum board, and
+        only one for TeleDongle and TeleMini.  All boards are shipped from
+        the factory pre-calibrated, but the procedures are documented here
+       in case they are ever needed.  Re-calibration is not supported by
+       AltosUI, you must connect to the board with a serial terminal program
+       and interact directly with the on-board command interpreter to effect
+       calibration.
+      </para>
+      <section>
+        <title>Radio Frequency</title>
+        <para>
+          The radio frequency is synthesized from a clock based on the 48 MHz
+          crystal on the board.  The actual frequency of this oscillator 
+          must be measured to generate a calibration constant.  While our 
+          GFSK modulation
+          bandwidth is wide enough to allow boards to communicate even when
+          their oscillators are not on exactly the same frequency, performance
+          is best when they are closely matched.
+          Radio frequency calibration requires a calibrated frequency counter.
+          Fortunately, once set, the variation in frequency due to aging and
+          temperature changes is small enough that re-calibration by customers
+          should generally not be required.
+        </para>
+        <para>
+          To calibrate the radio frequency, connect the UHF antenna port to a
+          frequency counter, set the board to 434.550MHz, and use the 'C'
+          command in the on-board command interpreter to generate a CW 
+          carrier.  For TeleMetrum, this is best done over USB.  For TeleMini,
+         note that the only way to escape the 'C' command is via power cycle
+         since the board will no longer be listening for commands once it
+         starts generating a CW carrier.
+       </para>
+       <para>
+         Wait for the transmitter temperature to stabilize and the frequency 
+          to settle down.  Then, divide 434.550 MHz by the
+          measured frequency and multiply by the current radio cal value show
+          in the 'c s' command.  For an unprogrammed board, the default value
+          is 1186611.  Take the resulting integer and program it using the 'c f'
+          command.  Testing with the 'C' command again should show a carrier
+          within a few tens of Hertz of the intended frequency.
+          As with all 'c' sub-commands, follow this with a 'c w' to write the
+          change to the parameter block in the on-board DataFlash chip.
+        </para>
+       <para>
+         Note that any time you re-do the radio frequency calibration, the
+         radio frequency is reset to the default 434.550 Mhz.  If you want
+         to use another frequency, you will have to set that again after
+         calibration is completed.
+       </para>
+      </section>
+      <section>
+        <title>TeleMetrum Accelerometer</title>
+        <para>
+          The TeleMetrum accelerometer we use has its own 5 volt power 
+         supply and
+          the output must be passed through a resistive voltage divider to match
+          the input of our 3.3 volt ADC.  This means that unlike the barometric
+          sensor, the output of the acceleration sensor is not ratio-metric to
+          the ADC converter, and calibration is required.  Explicitly 
+         calibrating the accelerometers also allows us to load any device
+         from a Freescale family that includes at least +/- 40g, 50g, 100g, 
+         and 200g parts.  Using gravity,
+          a simple 2-point calibration yields acceptable results capturing both
+          the different sensitivities and ranges of the different accelerometer
+          parts and any variation in power supply voltages or resistor values
+          in the divider network.
+        </para>
+        <para>
+          To calibrate the acceleration sensor, use the 'c a 0' command.  You
+          will be prompted to orient the board vertically with the UHF antenna
+          up and press a key, then to orient the board vertically with the
+          UHF antenna down and press a key.  Note that the accuracy of this
+         calibration depends primarily on how perfectly vertical and still
+         the board is held during the cal process.  As with all 'c' 
+         sub-commands, follow this with a 'c w' to write the
+          change to the parameter block in the on-board DataFlash chip.
+        </para>
+        <para>
+          The +1g and -1g calibration points are included in each telemetry
+          frame and are part of the header stored in onboard flash to be
+         downloaded after flight.  We always store and return raw ADC 
+         samples for each sensor... so nothing is permanently "lost" or 
+         "damaged" if the calibration is poor.
+        </para>
+        <para>
+         In the unlikely event an accel cal 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 link.  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),
+        allowing a re-cal.
+        </para>
+      </section>
+  </appendix>
+  <appendix
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+    <title>Release Notes</title>
+    <xi:include        href="release-notes-1.1.xsl"  xpointer="xpointer(/article/*)"/>
+    <xi:include        href="release-notes-1.0.1.xsl"  xpointer="xpointer(/article/*)"/>
+    <xi:include        href="release-notes-0.9.2.xsl"  xpointer="xpointer(/article/*)"/>
+    <xi:include        href="release-notes-0.9.xsl"  xpointer="xpointer(/article/*)"/>
+    <xi:include        href="release-notes-0.8.xsl"  xpointer="xpointer(/article/*)"/>
+    <xi:include        href="release-notes-0.7.1.xsl"  xpointer="xpointer(/article/*)"/>
+  </appendix>
+</book>
+
+<!--  LocalWords:  Altusmetrum
+-->
diff --git a/doc/companion.xsl b/doc/companion.xsl
new file mode 100644 (file)
index 0000000..1215d9a
--- /dev/null
@@ -0,0 +1,347 @@
+<?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">
+
+<article>
+  <articleinfo>
+    <title>AltOS Companion Port</title>
+    <subtitle>Protocol Definitions</subtitle>
+    <author>
+      <firstname>Keith</firstname>
+      <surname>Packard</surname>
+    </author>
+    <copyright>
+      <year>2012</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>13 January 2012</date>
+       <revremark>Initial content</revremark>
+      </revision>
+    </revhistory>
+  </articleinfo>
+  <section>
+    <title>Companion Port</title>
+    <para>
+      Many Altus Metrum products come with an eight pin Micro MaTch
+      connector, called the Companion Port. This is often used to
+      program devices using a programming cable. However, it can also
+      be used to connect TeleMetrum to external companion boards
+      (hence the name).
+    </para>
+    <para>
+      The Companion Port provides two different functions:
+      <itemizedlist>
+       <listitem>
+         Power. Both battery-level and 3.3V regulated power are
+         available. Note that the amount of regulated power is not
+         huge; TeleMetrum contains a 150mA regulator and uses, at
+         peak, about 120mA or so. For applications needing more than
+         a few dozen mA, placing a separate regulator on them and
+         using the battery for power is probably a good idea.
+       </listitem>
+       <listitem>
+         SPI. The flight computer operates as a SPI master, using
+         a protocol defined in this document. Companion boards
+         provide a matching SPI slave implementation which supplies
+         telemetry information for the radio downlink during flight
+       </listitem>
+      </itemizedlist>
+    </para>
+  </section>
+  <section>
+    <title>Companion SPI Protocol</title>
+    <para>
+      The flight computer implements a SPI master communications
+      channel over the companion port, and uses this to get
+      information about a connected companion board and then to get
+      telemetry data for transmission during flight.
+    </para>
+    <para>
+      At startup time, the flight computer sends a setup request
+      packet, and the companion board returns a board identifier, the
+      desired telemetry update period and the number of data channels
+      provided. The flight computer doesn't interpret the telemetry
+      data at all, simply packing it up and sending it over the link.
+      Telemetry packets are 32 bytes long, and companion packets use 8
+      bytes as a header leaving room for a maximum of 12 16-bit data
+      values.
+    </para>
+    <para>
+      Because of the limits of the AVR processors used in the first
+      two companion boards, the SPI data rate is set to 187.5kbaud.
+    </para>
+  </section>
+  <section>
+    <title>SPI Message Formats</title>
+    This section first defines the command message format sent from
+    the flight computer to the companion board, and then the various
+    reply message formats for each type of command message.
+    <section>
+      <title>Command Message</title>
+      <table frame='all'>
+       <title>Companion Command Message</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0</entry>
+             <entry>uint8_t</entry>
+             <entry>command</entry>
+             <entry>Command identifier</entry>
+           </row>
+           <row>
+             <entry>1</entry>
+             <entry>uint8_t</entry>
+             <entry>flight_state</entry>
+             <entry>Current flight computer state</entry>
+           </row>
+           <row>
+             <entry>2</entry>
+             <entry>uint16_t</entry>
+             <entry>tick</entry>
+             <entry>Flight computer clock (100 ticks/second)</entry>
+           </row>
+           <row>
+             <entry>4</entry>
+             <entry>uint16_t</entry>
+             <entry>serial</entry>
+             <entry>Flight computer serial number</entry>
+           </row>
+           <row>
+             <entry>6</entry>
+             <entry>uint16_t</entry>
+             <entry>flight</entry>
+             <entry>Flight number</entry>
+           </row>
+           <row>
+             <entry>8</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <table frame='all'>
+       <title>Companion Command Identifiers</title>
+       <tgroup cols='3' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Value'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry>Value</entry>
+             <entry>Name</entry>
+             <entry>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>1</entry>
+             <entry>SETUP</entry>
+             <entry>Supply the flight computer with companion
+             information</entry>
+           </row>
+           <row>
+             <entry>2</entry>
+             <entry>FETCH</entry>
+             <entry>Return telemetry information</entry>
+           </row>
+           <row>
+             <entry>3</entry>
+             <entry>NOTIFY</entry>
+             <entry>Tell companion board when flight state
+             changes</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <para>
+       The flight computer will send a SETUP message shortly after
+       power-up and will then send FETCH messages no more often than
+       the rate specified in the SETUP reply. NOTIFY messages will be
+       sent whenever the flight state changes.
+      </para>
+      <para>
+       'flight_state' records the current state of the flight,
+       whether on the pad, under power, coasting to apogee or
+       descending on the drogue or main chute.
+      </para>
+      <para>
+       'tick' provides the current flight computer clock, which 
+       be used to synchronize data recorded on the flight computer
+       with that recorded on the companion board in post-flight analysis.
+      </para>
+      <para>
+       'serial' is the product serial number of the flight computer,
+       'flight' is the flight sequence number. Together, these two
+       uniquely identify the flight and can be recorded with any
+       companion board data logging to associate the companion data
+       with the proper flight.
+      </para>
+      <para>
+       NOTIFY commands require no reply at all, they are used solely
+       to inform the companion board when the state of the flight, as
+       computed by the flight computer, changes. Companion boards can
+       use this to change data collection parameters, disabling data
+       logging until the flight starts and terminating it when the
+       flight ends.
+      </para>
+    </section>
+    <section>
+      <title>SETUP reply message</title>
+      <table frame='all'>
+       <title>SETUP reply contents</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0</entry>
+             <entry>uint16_t</entry>
+             <entry>board_id</entry>
+             <entry>Board identifier</entry>
+           </row>
+           <row>
+             <entry>2</entry>
+             <entry>uint16_t</entry>
+             <entry>board_id_inverse</entry>
+             <entry>~board_id—used to tell if a board is present</entry>
+           </row>
+           <row>
+             <entry>4</entry>
+             <entry>uint8_t</entry>
+             <entry>update_period</entry>
+             <entry>Minimum time (in 100Hz ticks) between FETCH commands</entry>
+           </row>
+           <row>
+             <entry>5</entry>
+             <entry>uint8_t</entry>
+             <entry>channels</entry>
+             <entry>Number of data channels to retrieve in FETCH command</entry>
+           </row>
+           <row>
+             <entry>6</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <para>
+       The SETUP reply contains enough information to uniquely
+       identify the companion board to the end user as well as for
+       the flight computer to know how many data values to expect in
+       reply to a FETCH command, and how often to fetch that data.
+      </para>
+      <para>
+       To detect the presence of a companion board, the flight
+       computer checks to make sure that board_id_inverse is the
+       bit-wise inverse of board_id. Current companion boards use
+       USB product ID as the board_id, but the flight computer does
+       not interpret this data and so it can be any value.
+      </para>
+    </section>
+    <section>
+      <title>FETCH reply message</title>
+      <table frame='all'>
+       <title>FETCH reply contents</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0</entry>
+             <entry>uint16_t</entry>
+             <entry>data0</entry>
+             <entry>0th data item</entry>
+           </row>
+           <row>
+             <entry>2</entry>
+             <entry>uint16_t</entry>
+             <entry>data1</entry>
+             <entry>1st data item</entry>
+           </row>
+           <row>
+             <entry>...</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <para>
+       The FETCH reply contains arbitrary data to be reported over
+       the flight computer telemetry link. The number of 16-bit data items
+       must match the 'channels' value provided in the SETUP reply
+       message.
+      </para>
+    </section>
+  </section>
+  <section>
+    <title>History and Motivation</title>
+    <para>
+      To allow cross-programming, the original TeleMetrum and
+      TeleDongle designs needed to include some kind of
+      connector. With that in place, adding the ability to connect
+      external cards to TeleMetrum was fairly simple. We set the
+      software piece of this puzzle aside until we had a companion
+      board to use.
+    </para>
+    <para>
+      The first companion board was TeleScience. Designed to collect
+      temperature data from the nose and fin of the airframe, the main
+      requirement for the companion port was that it be able to report
+      telemetry data during flight as a back-up in case the
+      TeleScience on-board data was lost.
+    </para>
+    <para>
+      The second companion board, TelePyro, provides 8 additional
+      channels for deployment, staging or other activities. To avoid
+      re-programming the TeleMetrum to use TelePyro, we decided to
+      provide enough information over the companion link for it to
+      independently control those channels.
+    </para>
+    <para>
+      Providing a standard, constant interface between the flight
+      computer and companion boards allows for the base flight
+      computer firmware to include support for companion boards.
+    </para>
+  </section>
+</article>
diff --git a/doc/megametrum-outline.pdf b/doc/megametrum-outline.pdf
new file mode 100644 (file)
index 0000000..f8fc26e
Binary files /dev/null and b/doc/megametrum-outline.pdf differ
diff --git a/doc/megametrum-outline.svg b/doc/megametrum-outline.svg
new file mode 100644 (file)
index 0000000..e8d74d3
--- /dev/null
@@ -0,0 +1,244 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="427.5"
+   height="270"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.3.1 r9886"
+   sodipodi:docname="megametrum-outline.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow2Lend"
+       style="overflow:visible;">
+      <path
+         id="path3866"
+         style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+         transform="scale(1.1) rotate(180) translate(1,0)" />
+    </marker>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.1783851"
+     inkscape:cx="315.40175"
+     inkscape:cy="122.33575"
+     inkscape:document-units="in"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:window-width="1527"
+     inkscape:window-height="1313"
+     inkscape:window-x="813"
+     inkscape:window-y="166"
+     inkscape:window-maximized="0"
+     units="in"
+     showguides="true"
+     inkscape:guide-bbox="true">
+    <sodipodi:guide
+       position="0,0"
+       orientation="0,427.5"
+       id="guide3005" />
+    <sodipodi:guide
+       position="427.5,0"
+       orientation="-270,0"
+       id="guide3007" />
+    <sodipodi:guide
+       position="427.5,270"
+       orientation="0,-427.5"
+       id="guide3009" />
+    <sodipodi:guide
+       position="0,270"
+       orientation="270,0"
+       id="guide3011" />
+    <inkscape:grid
+       type="xygrid"
+       id="grid3013"
+       empspacing="4"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       units="in"
+       spacingx="0.025in"
+       spacingy="0.025in" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-782.35975)">
+    <rect
+       style="fill:none;stroke:#000000;stroke-width:0.54555845;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2816"
+       width="291.95444"
+       height="111.95444"
+       x="90.272781"
+       y="850.13251" />
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3601"
+       transform="matrix(0.97131843,0,0,0.97528987,8.397686,191.32255)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3611"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3613"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3615"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3603"
+       transform="matrix(0.97186116,0,0,0.97241431,278.34851,193.3134)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3619"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3621"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3623"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3605"
+       transform="matrix(0.97475506,0,0,0.97241431,278.08835,283.31323)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3627"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3629"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3631"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3607"
+       transform="matrix(0.97186116,0,0,0.97241431,8.3485628,283.31356)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3635"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3637"
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3639"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         inkscape:connector-curvature="0" />
+    </g>
+    <path
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.79999995;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+       d="m 135,903.85975 157.5,0"
+       id="path2829"
+       sodipodi:nodetypes="cc"
+       inkscape:connector-curvature="0" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Minion Pro;-inkscape-font-specification:Minion Pro"
+       x="888.10974"
+       y="-303.75"
+       id="text4236"
+       sodipodi:linespacing="125%"
+       transform="matrix(0,1,-1,0,0,0)"><tspan
+         sodipodi:role="line"
+         x="888.10974"
+         y="-303.75"
+         id="tspan4242">UP</tspan></text>
+  </g>
+</svg>
diff --git a/doc/release-notes-0.7.1.xsl b/doc/release-notes-0.7.1.xsl
new file mode 100644 (file)
index 0000000..75158a0
--- /dev/null
@@ -0,0 +1,57 @@
+<?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">
+
+<article>
+  <para>
+Version 0.7.1 is the first release containing our new cross-platform Java-based user interface. AltosUI can:
+  </para>
+  <itemizedlist>
+    <listitem>
+      Receive and log telemetry from a connected TeleDongle
+      device. All data received is saved to log files named with the
+      current date and the connected rocket serial and flight
+      numbers. There is no mode in which telemetry data will not be
+      saved.
+    </listitem>
+    <listitem>
+      Download logged data from TeleMetrum devices, either through a
+      direct USB connection or over the air through a TeleDongle
+      device.
+    </listitem>
+    <listitem>
+      Configure a TeleMetrum device, setting the radio channel,
+      callsign, apogee delay and main deploy height. This can be done
+      through either a USB connection or over a radio link via a
+      TeleDongle device.
+    </listitem>
+    <listitem>
+      Replay a flight in real-time. This takes a saved telemetry log
+      or eeprom download and replays it through the user interface so
+      you can relive your favorite rocket flights.
+    </listitem>
+    <listitem>
+      Reprogram Altus Metrum devices. Using an Altus Metrum device
+      connected via USB, another Altus Metrum device can be
+      reprogrammed using the supplied programming cable between the
+      two devices.
+    </listitem>
+    <listitem>
+      Export Flight data to a comma-separated-values file. This takes
+      either telemetry or on-board flight data and generates data
+      suitable for use in external applications. All data is exported
+      using standard units so that no device-specific knowledge is
+      needed to handle the data.
+    </listitem>
+    <listitem>
+      Speak to you during the flight. Instead of spending the flight
+      hunched over your laptop looking at the screen, enjoy the view
+      while the computer tells you what’s going on up there. During
+      ascent, you hear the current flight state and altitude
+      information. During descent, you get azimuth, elevation and
+      range information to try and help you find your rocket in the
+      air. Once on the ground, the direction and distance are
+      reported.
+    </listitem>
+  </itemizedlist>
+</article>
diff --git a/doc/release-notes-0.8.xsl b/doc/release-notes-0.8.xsl
new file mode 100644 (file)
index 0000000..c54f97e
--- /dev/null
@@ -0,0 +1,56 @@
+<?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">
+
+<article>
+  <para>
+    Version 0.8 offers a major upgrade in the AltosUI
+    interface. Significant new features include:
+  </para>
+  <itemizedlist>
+    <listitem>
+      Post-flight graphing tool. This lets you explore the behaviour
+      of your rocket after flight with a scroll-able and zoom-able
+      chart showing the altitude, speed and acceleration of the
+      airframe along with events recorded by the flight computer. You
+      can export graphs to PNG files, or print them directly.
+    </listitem>
+    <listitem>
+      Real-time moving map which overlays the in-progress flight on
+      satellite imagery fetched from Google Maps. This lets you see in
+      pictures where your rocket has landed, allowing you to plan
+      recovery activities more accurately.
+    </listitem>
+    <listitem>
+      Wireless recovery system testing. Prep your rocket for flight
+      and test fire the deployment charges to make sure things work as
+      expected. All without threading wires through holes in your
+      airframe.
+    </listitem>
+    <listitem>
+      Optimized flight status displays. Each flight state now has it's
+      own custom 'tab' in the flight monitoring window so you can
+      focus on the most important details. Pre-flight, the system
+      shows a set of red/green status indicators for battery voltage,
+      apogee/main igniter continutity and GPS reception. Wait until
+      they're all green and your rocket is ready for flight. There are
+      also tabs for ascent, descent and landing along with the
+      original tabular view of the data.
+    </listitem>
+    <listitem>
+      Monitor multiple flights simultaneously. If you have more than
+      one TeleDongle, you can monitor a flight with each one on the
+      same computer.
+    </listitem>
+    <listitem>
+      Automatic flight monitoring at startup. Plug TeleDongle into the
+      machine before starting AltosUI and it will automatically
+      connect to it and prepare to monitor a flight.
+    </listitem>
+    <listitem>
+      Exports Google Earth flight tracks. Using the Keyhole Markup
+      Language (.kml) file format, this provides a 3D view of your
+      rocket flight through the Google Earth program.
+    </listitem>
+  </itemizedlist>
+</article>
diff --git a/doc/release-notes-0.9.2.xsl b/doc/release-notes-0.9.2.xsl
new file mode 100644 (file)
index 0000000..e5f66c6
--- /dev/null
@@ -0,0 +1,20 @@
+<?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">
+
+<article>
+  <para>
+    Version 0.9.2 is an AltosUI bug-fix release, with no firmware changes.
+  </para>
+  <itemizedlist>
+    <listitem>
+      Fix plotting problems due to missing file in the Mac OS install image.
+    </listitem>
+    <listitem>
+      Always read whole eeprom blocks, mark empty records invalid, display parsing errors to user.
+    </listitem>
+    <listitem>
+      Add software version to Configure AltosUI dialog
+    </listitem>
+  </itemizedlist>
+</article>
diff --git a/doc/release-notes-0.9.xsl b/doc/release-notes-0.9.xsl
new file mode 100644 (file)
index 0000000..547f46b
--- /dev/null
@@ -0,0 +1,31 @@
+<?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">
+
+<article>
+  <para>
+    Version 0.9 adds a few new firmware features and accompanying
+    AltosUI changes, along with new hardware support.
+  </para>
+  <itemizedlist>
+    <listitem>
+      Support for TeleMetrum v1.1 hardware. Sources for the flash
+      memory part used in v1.0 dried up, so v1.1 uses a different part
+      which required a new driver and support for explicit flight log
+      erasing.
+    </listitem>
+    <listitem>
+      Multiple flight log support. This stores more than one flight
+      log in the on-board flash memory. It also requires the user to
+      explicitly erase flights so that you won't lose flight logs just
+      because you fly the same board twice in one day.
+    </listitem>
+    <listitem>
+      Telemetry support for devices with serial number >=
+      256. Previous versions used a telemetry packet format that
+      provided only 8 bits for the device serial number. This change
+      requires that both ends of the telemetry link be running the 0.9
+      firmware or they will not communicate.
+    </listitem>
+  </itemizedlist>
+</article>
diff --git a/doc/release-notes-1.0.1.xsl b/doc/release-notes-1.0.1.xsl
new file mode 100644 (file)
index 0000000..1e9fcab
--- /dev/null
@@ -0,0 +1,103 @@
+<?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">
+
+<article>
+  <para>
+    Version 1.0.1 is a major release, adding support for the TeleMini
+    device and lots of new AltosUI features
+  </para>
+  <para>
+    AltOS Firmware Changes
+    <itemizedlist>
+      <listitem>
+       Add TeleMini v1.0 support. Firmware images for TeleMini are
+       included in AltOS releases.
+      </listitem>
+      <listitem>
+       Change telemetry to be encoded in multiple 32-byte packets. This
+       enables support for TeleMini and other devices without requiring
+       further updates to the TeleDongle firmware.
+      </listitem>
+      <listitem>
+       Support operation of TeleMetrum with the antenna pointing
+       aft. Previous firmware versions required the antenna to be
+       pointing upwards, now there is a configuration option allowing
+       the antenna to point aft, to aid installation in some airframes.
+      </listitem>
+      <listitem>
+       Ability to disable telemetry. For airframes where an antenna
+       just isn't possible, or where radio transmissions might cause
+       trouble with other electronics, there's a configuration option
+       to disable all telemetry. Note that the board will still
+       enable the radio link in idle mode.
+      </listitem>
+      <listitem>
+       Arbitrary frequency selection. The radios in Altus Metrum
+       devices can be programmed to a wide range of frequencies, so
+       instead of limiting devices to 10 pre-selected 'channels', the
+       new firmware allows the user to choose any frequency in the
+       70cm band. Note that the RF matching circuit on the boards is
+       tuned for around 435MHz, so frequencies far from that may
+       reduce the available range.
+      </listitem>
+      <listitem>
+       Kalman-filter based flight-tracking. The model based sensor
+       fusion approach of a Kalman filter means that AltOS now
+       computes apogee much more accurately than before, generally
+       within a fraction of a second. In addition, this approach
+       allows the baro-only TeleMini device to correctly identify
+       Mach transitions, avoiding the error-prone selection of a Mach
+       delay.
+      </listitem>
+    </itemizedlist>
+  </para>
+  <para>
+    AltosUI Changes
+    <itemizedlist>
+      <listitem>
+       Wait for altimeter when using packet mode. Instead of quicly
+       timing out when trying to initialize a packet mode
+       configuration connection, AltosUI now waits indefinitely for
+       the remote device to appear, providing a cancel button should
+       the user get bored. This is necessary as the TeleMini can only
+       be placed in "Idle" mode if AltosUI is polling it.
+      </listitem>
+      <listitem>
+       Add main/apogee voltage graphs to the data plot. This provides
+       a visual indication if the igniters fail before being fired.
+      </listitem>
+      <listitem>
+       Scan for altimeter devices by watching the defined telemetry
+       frequencies. This avoids the problem of remembering what
+       frequency a device was configured to use, which is especially
+       important with TeleMini which does not include a USB connection.
+      </listitem>
+      <listitem>
+       Monitor altimeter state in "Idle" mode. This provides much of
+       the information presented in the "Pad" dialog from the Monitor
+       Flight command, monitoring the igniters, battery and GPS
+       status withing requiring the flight computer to be armed and
+       ready for flight.
+      </listitem>
+      <listitem>
+       Pre-load map images from home. For those launch sites which
+       don't provide free Wi-Fi, this allows you to download the
+       necessary satellite images given the location of the launch
+       site. A list of known launch sites is maintained at
+       altusmetrum.org which AltosUI downloads to populate a menu; if
+       you've got a launch site not on that list, please send the
+       name of it, latitude and longitude along with a link to the
+       web site of the controlling club to the altusmetrum mailing list.
+      </listitem>
+      <listitem>
+       Flight statistics are now displayed in the Graph data
+       window. These include max height/speed/accel, average descent
+       rates and a few other bits of information. The Graph Data
+       window can now be reached from the 'Landed' tab in the Monitor
+       Flight window so you can immediately see the results of a
+       flight.
+      </listitem>
+    </itemizedlist>
+  </para>
+</article>
diff --git a/doc/release-notes-1.1.xsl b/doc/release-notes-1.1.xsl
new file mode 100644 (file)
index 0000000..79ea39e
--- /dev/null
@@ -0,0 +1,94 @@
+<?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">
+
+<article>
+  <para>
+    Version 1.1 is a minor release. It provides a few new features in AltosUI
+    and the AltOS firmware and fixes bugs.
+  </para>
+  <para>
+    AltOS Firmware Changes
+    <itemizedlist>
+      <listitem>
+       Add apogee-lockout value. Overrides the apogee detection logic to
+       prevent incorrect apogee charge firing.
+      </listitem>
+      <listitem>
+       Fix a bug where the data reported in telemetry packets was
+       from 320ms ago.
+      </listitem>
+      <listitem>
+       Force the radio frequency to 434.550MHz when the debug clock
+       pin is connected to ground at boot time. This provides a way
+       to talk to a TeleMini which is configured to some unknown frequency.
+      </listitem>
+      <listitem>
+       Provide RSSI values for Monitor Idle mode. This makes it easy to check radio
+       range without needing to go to flight mode.
+      </listitem>
+      <listitem>
+       Fix a bug which caused the old received telemetry packets to
+       be retransmitted over the USB link when the radio was turned
+       off and back on.
+      </listitem>
+    </itemizedlist>
+  </para>
+  <para>
+    AltosUI Changes
+    <itemizedlist>
+      <listitem>
+       Fix a bug that caused GPS ready to happen too quickly. The
+       software was using every telemetry packet to signal new GPS
+       data, which caused GPS ready to be signalled after 10 packets
+       instead of 10 GPS updates.
+      </listitem>
+      <listitem>
+       Fix Google Earth data export to work with recent versions. The
+       google earth file loading code got a lot pickier, requiring
+       some minor white space changes in the export code.
+      </listitem>
+      <listitem>
+       Make the look-n-feel configurable, providing a choice from
+       the available options.
+      </listitem>
+      <listitem>
+       Add an 'Age' element to mark how long since a telemetry packet
+       has been received. Useful to quickly gauge whether
+       communications with the rocket are still active.
+      </listitem>
+      <listitem>
+       Add 'Configure Ground Station' dialog to set the radio
+       frequency used by a particular TeleDongle without having to go
+       through the flight monitor UI.
+      </listitem>
+      <listitem>
+       Add configuration for the new apogee-lockout value. A menu provides a list of
+       reasonable values, or the value can be set by hand.
+      </listitem>
+      <listitem>
+       Re-compute time spent in each state for the flight graph; this
+       figures out the actual boost and landing times instead of
+       using the conservative values provide by the flight
+       electronics. This improves the accuracy of the boost
+       acceleration and main descent rate computations.
+      </listitem>
+      <listitem>
+       Make AltosUI run on Mac OS Lion. The default Java heap space
+       was dramatically reduced for this release causing much of the
+       UI to fail randomly. This most often affected the satellite
+       mapping download and displays.
+      </listitem>
+      <listitem>
+       Change how data are displayed in the 'table' tab of the flight
+       monitoring window. This eliminates entries duplicated from the
+       header and adds both current altitude and pad altitude, which
+       are useful in 'Monitor Idle' mode.
+      </listitem>
+      <listitem>
+       Add Imperial units mode to present data in feet instead of
+       meters.
+      </listitem>
+    </itemizedlist>
+  </para>
+</article>
diff --git a/doc/telemetrum-outline.pdf b/doc/telemetrum-outline.pdf
new file mode 100644 (file)
index 0000000..09ce557
Binary files /dev/null and b/doc/telemetrum-outline.pdf differ
diff --git a/doc/telemetrum-outline.svg b/doc/telemetrum-outline.svg
new file mode 100644 (file)
index 0000000..aee63ed
--- /dev/null
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="427.5"
+   height="270"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.47 r22583"
+   sodipodi:docname="telemetrum-outline.svg">
+  <defs
+     id="defs4">
+    <marker
+       inkscape:stockid="Arrow2Lend"
+       orient="auto"
+       refY="0.0"
+       refX="0.0"
+       id="Arrow2Lend"
+       style="overflow:visible;">
+      <path
+         id="path3866"
+         style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
+         d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
+         transform="scale(1.1) rotate(180) translate(1,0)" />
+    </marker>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 526.18109 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="744.09448 : 526.18109 : 1"
+       inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+       id="perspective10" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1.1459933"
+     inkscape:cx="182.36411"
+     inkscape:cy="261.60668"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1146"
+     inkscape:window-height="846"
+     inkscape:window-x="0"
+     inkscape:window-y="20"
+     inkscape:window-maximized="0"
+     units="in" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-782.35975)">
+    <rect
+       style="fill:none;stroke:#000000;stroke-width:0.44999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+       id="rect2816"
+       width="247.38385"
+       height="89.893326"
+       x="90.0625"
+       y="872.40637" />
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3601"
+       transform="translate(28.141535,264.43715)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3611"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3613"
+         sodipodi:nodetypes="cc" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3615"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3603"
+       transform="translate(230.64154,196.89215)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3619"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3621"
+         sodipodi:nodetypes="cc" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3623"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3605"
+       transform="translate(230.64154,264.39215)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3627"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3629"
+         sodipodi:nodetypes="cc" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3631"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+    <g
+       inkscape:tile-y0="681.11218"
+       inkscape:tile-x0="90"
+       id="use3607"
+       transform="translate(28.141535,196.89215)">
+      <path
+         sodipodi:type="arc"
+         style="fill:none;stroke:#000000;stroke-width:1.41507304;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+         id="path3635"
+         sodipodi:cx="116"
+         sodipodi:cy="739.36218"
+         sodipodi:rx="17"
+         sodipodi:ry="18"
+         d="m 133,739.36218 c 0,9.94113 -7.61116,18 -17,18 -9.38884,0 -17,-8.05887 -17,-18 0,-9.94112 7.61116,-18 17,-18 9.38884,0 17,8.05888 17,18 z"
+         transform="matrix(0.32722728,0,0,0.3090422,57.632964,458.24316)" />
+      <path
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.10785132;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
+         d="m 95.625,692.36218 0,-11.25"
+         id="path3637"
+         sodipodi:nodetypes="cc" />
+      <path
+         sodipodi:nodetypes="cc"
+         id="path3639"
+         d="m 90,686.73718 11.25,0"
+         style="color:#000000;fill:#67615b;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.09;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
+    </g>
+    <path
+       style="color:#000000;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.80000000000000004;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;marker-end:url(#Arrow2Lend)"
+       d="m 135,135 157.5,0"
+       id="path2829"
+       transform="translate(0,782.35975)"
+       sodipodi:nodetypes="cc" />
+    <text
+       xml:space="preserve"
+       style="font-size:22px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Minion Pro;-inkscape-font-specification:Minion Pro"
+       x="903.51935"
+       y="-305.87912"
+       id="text4236"
+       sodipodi:linespacing="125%"
+       transform="matrix(0,1,-1,0,0,0)"><tspan
+         sodipodi:role="line"
+         x="903.51935"
+         y="-305.87912"
+         id="tspan4242">UP</tspan></text>
+  </g>
+</svg>
diff --git a/doc/telemetry.xsl b/doc/telemetry.xsl
new file mode 100644 (file)
index 0000000..fa66bff
--- /dev/null
@@ -0,0 +1,806 @@
+<?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">
+
+<article>
+  <articleinfo>
+    <title>AltOS Telemetry</title>
+    <subtitle>Packet Definitions</subtitle>
+    <author>
+      <firstname>Keith</firstname>
+      <surname>Packard</surname>
+    </author>
+    <copyright>
+      <year>2011</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>01 July 2011</date>
+       <revremark>Initial content</revremark>
+      </revision>
+    </revhistory>
+  </articleinfo>
+  <section>
+    <title>Packet Format Design</title>
+    <para>
+      AltOS telemetry data is split into multiple different packets,
+      all the same size, but each includs an identifier so that the
+      ground station can distinguish among different types. A single
+      flight board will transmit multiple packet types, each type on a
+      different schedule. The ground software need look for only a
+      single packet size, and then decode the information within the
+      packet and merge data from multiple packets to construct the
+      full flight computer state.
+    </para>
+    <para>
+      Each AltOS packet is 32 bytes long. This size was chosen based
+      on the known telemetry data requirements. The power of two size
+      allows them to be stored easily in flash memory without having
+      them split across blocks or leaving gaps at the end.
+    </para>
+    <para>
+      All packet types start with a five byte header which encodes the
+      device serial number, device clock value and the packet
+      type. The remaining 27 bytes encode type-specific data.
+    </para>
+  </section>
+  <section>
+    <title>Packet Formats</title>
+    This section first defines the packet header common to all packets
+    and then the per-packet data layout.
+    <section>
+      <title>Packet Header</title>
+      <table frame='all'>
+       <title>Telemetry Packet Header</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0</entry>
+             <entry>uint16_t</entry>
+             <entry>serial</entry>
+             <entry>Device serial Number</entry>
+           </row>
+           <row>
+             <entry>2</entry>
+             <entry>uint16_t</entry>
+             <entry>tick</entry>
+             <entry>Device time in 100ths of a second</entry>
+           </row>
+           <row>
+             <entry>4</entry>
+             <entry>uint8_t</entry>
+             <entry>type</entry>
+             <entry>Packet type</entry>
+           </row>
+           <row>
+             <entry>5</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <para>
+      Each packet starts with these five bytes which serve to identify
+      which device has transmitted the packet, when it was transmitted
+      and what the rest of the packet contains.
+      </para>
+    </section>
+    <section>
+      <title>Sensor Data</title>
+      <informaltable frame='none' label='' tocentry='0'>
+       <tgroup cols='2' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='left' colwidth='3*' colname='Description'/>
+         <thead>
+           <row>
+             <entry>Type</entry>
+             <entry>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0x01</entry>
+             <entry>TeleMetrum Sensor Data</entry>
+           </row>
+           <row>
+             <entry>0x02</entry>
+             <entry>TeleMini Sensor Data</entry>
+           </row>
+           <row>
+             <entry>0x03</entry>
+             <entry>TeleNano Sensor Data</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </informaltable>
+      <para>
+       TeleMetrum, TeleMini and TeleNano share this same packet
+       format for sensor data. Each uses a distinct packet type so
+       that the receiver knows which data values are valid and which
+       are undefined.
+      </para>
+      <para>
+       Sensor Data packets are transmitted once per second on the
+       ground, 10 times per second during ascent and once per second
+       during descent and landing
+      </para>
+      <table frame='all'>
+       <title>Sensor Packet Contents</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>5</entry><entry>uint8_t</entry><entry>state</entry><entry>Flight state</entry>
+           </row>
+           <row>
+             <entry>6</entry><entry>int16_t</entry><entry>accel</entry><entry>accelerometer (TM only)</entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>int16_t</entry><entry>pres</entry><entry>pressure sensor</entry>
+           </row>
+           <row>
+             <entry>10</entry><entry>int16_t</entry><entry>temp</entry><entry>temperature sensor</entry>
+           </row>
+           <row>
+             <entry>12</entry><entry>int16_t</entry><entry>v_batt</entry><entry>battery voltage</entry>
+           </row>
+           <row>
+             <entry>14</entry><entry>int16_t</entry><entry>sense_d</entry><entry>drogue continuity sense (TM/Tm)</entry>
+           </row>
+           <row>
+             <entry>16</entry><entry>int16_t</entry><entry>sense_m</entry><entry>main continuity sense (TM/Tm)</entry>
+           </row>
+           <row>
+             <entry>18</entry><entry>int16_t</entry><entry>acceleration</entry><entry>m/s² * 16</entry>
+           </row>
+           <row>
+             <entry>20</entry><entry>int16_t</entry><entry>speed</entry><entry>m/s * 16</entry>
+           </row>
+           <row>
+             <entry>22</entry><entry>int16_t</entry><entry>height</entry><entry>m</entry>
+           </row>
+           <row>
+             <entry>24</entry><entry>int16_t</entry><entry>ground_pres</entry><entry>Average barometer reading on ground</entry>
+           </row>
+           <row>
+             <entry>26</entry><entry>int16_t</entry><entry>ground_accel</entry><entry>TM</entry>
+           </row>
+           <row>
+             <entry>28</entry><entry>int16_t</entry><entry>accel_plus_g</entry><entry>TM</entry>
+           </row>
+           <row>
+             <entry>30</entry><entry>int16_t</entry><entry>accel_minus_g</entry><entry>TM</entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
+    <section>
+      <title>Configuration Data</title>
+      <informaltable frame='none' label='' tocentry='0'>
+       <tgroup cols='2' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='left' colwidth='3*' colname='Description'/>
+         <thead>
+           <row>
+             <entry>Type</entry>
+             <entry>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0x04</entry>
+             <entry>Configuration Data</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </informaltable>
+      <para>
+       This provides a description of the software installed on the
+       flight computer as well as any user-specified configuration data.
+      </para>
+      <para>
+       Configuration data packets are transmitted once per second
+       during all phases of the flight
+      </para>
+      <table frame='all'>
+       <title>Sensor Packet Contents</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>5</entry><entry>uint8_t</entry><entry>type</entry><entry>Device type</entry>
+           </row>
+           <row>
+             <entry>6</entry><entry>uint16_t</entry><entry>flight</entry><entry>Flight number</entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>uint8_t</entry><entry>config_major</entry><entry>Config major version</entry>
+           </row>
+           <row>
+             <entry>9</entry><entry>uint8_t</entry><entry>config_minor</entry><entry>Config minor version</entry>
+           </row>
+           <row>
+             <entry>10</entry><entry>uint16_t</entry><entry>apogee_delay</entry>
+             <entry>Apogee deploy delay in seconds</entry>
+           </row>
+           <row>
+             <entry>12</entry><entry>uint16_t</entry><entry>main_deploy</entry><entry>Main deploy alt in meters</entry>
+           </row>
+           <row>
+             <entry>14</entry><entry>uint16_t</entry><entry>flight_log_max</entry>
+             <entry>Maximum flight log size (kB)</entry>
+           </row>
+           <row>
+             <entry>16</entry><entry>char</entry><entry>callsign[8]</entry><entry>Radio operator identifier</entry>
+           </row>
+           <row>
+             <entry>24</entry><entry>char</entry><entry>version[8]</entry><entry>Software version identifier</entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
+    <section>
+      <title>GPS Location</title>
+      <informaltable frame='none' label='' tocentry='0'>
+       <tgroup cols='2' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='left' colwidth='3*' colname='Description'/>
+         <thead>
+           <row>
+             <entry>Type</entry>
+             <entry>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0x05</entry>
+             <entry>GPS Location</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </informaltable>
+      <para>
+       This packet provides all of the information available from the
+       Venus SkyTraq GPS receiver—position, time, speed and precision
+       estimates. 
+      </para>
+      <para>
+       GPS Location packets are transmitted once per second during
+       all phases of the flight
+      </para>
+      <table frame='all'>
+       <title>GPS Location Packet Contents</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>5</entry><entry>uint8_t</entry><entry>flags</entry>
+             <entry>See GPS Flags table below</entry>
+           </row>
+           <row>
+             <entry>6</entry><entry>int16_t</entry><entry>altitude</entry><entry>m</entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>int32_t</entry><entry>latitude</entry><entry>degrees * 10<superscript>7</superscript></entry>
+           </row>
+           <row>
+             <entry>12</entry><entry>int32_t</entry><entry>longitude</entry><entry>degrees * 10<superscript>7</superscript></entry>
+           </row>
+           <row>
+             <entry>16</entry><entry>uint8_t</entry><entry>year</entry>
+           </row>
+           <row>
+             <entry>17</entry><entry>uint8_t</entry><entry>month</entry>
+           </row>
+           <row>
+             <entry>18</entry><entry>uint8_t</entry><entry>day</entry>
+           </row>
+           <row>
+             <entry>19</entry><entry>uint8_t</entry><entry>hour</entry>
+           </row>
+           <row>
+             <entry>20</entry><entry>uint8_t</entry><entry>minute</entry>
+           </row>
+           <row>
+             <entry>21</entry><entry>uint8_t</entry><entry>second</entry>
+           </row>
+           <row>
+             <entry>22</entry><entry>uint8_t</entry><entry>pdop</entry><entry>* 5</entry>
+           </row>
+           <row>
+             <entry>23</entry><entry>uint8_t</entry><entry>hdop</entry><entry>* 5</entry>
+           </row>
+           <row>
+             <entry>24</entry><entry>uint8_t</entry><entry>vdop</entry><entry>* 5</entry>
+           </row>
+           <row>
+             <entry>25</entry><entry>uint8_t</entry><entry>mode</entry>
+             <entry>See GPS Mode table below</entry>
+           </row>
+           <row>
+             <entry>26</entry><entry>uint16_t</entry><entry>ground_speed</entry><entry>cm/s</entry>
+           </row>
+           <row>
+             <entry>28</entry><entry>int16_t</entry><entry>climb_rate</entry><entry>cm/s</entry>
+           </row>
+           <row>
+             <entry>30</entry><entry>uint8_t</entry><entry>course</entry><entry>/ 2</entry>
+           </row>
+           <row>
+             <entry>31</entry><entry>uint8_t</entry><entry>unused[1]</entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <para>
+       Packed into a one byte field are status flags and the count of
+       satellites used to compute the position fix. Note that this
+       number may be lower than the number of satellites being
+       tracked; the receiver will not use information from satellites
+       with weak signals or which are close enough to the horizon to
+       have significantly degraded position accuracy.
+      </para>
+      <table frame='all'>
+       <title>GPS Flags</title>
+       <tgroup cols='3' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='bits'/>
+         <colspec align='left' colwidth='2*' colname='name'/>
+         <colspec align='left' colwidth='7*' colname='description'/>
+         <thead>
+           <row>
+             <entry align='center'>Bits</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0-3</entry>
+             <entry>nsats</entry>
+             <entry>Number of satellites in solution</entry>
+           </row>
+           <row>
+             <entry>4</entry>
+             <entry>valid</entry>
+             <entry>GPS solution is valid</entry>
+           </row>
+           <row>
+             <entry>5</entry>
+             <entry>running</entry>
+             <entry>GPS receiver is operational</entry>
+           </row>
+           <row>
+             <entry>6</entry>
+             <entry>date_valid</entry>
+             <entry>Reported date is valid</entry>
+           </row>
+           <row>
+             <entry>7</entry>
+             <entry>course_valid</entry>
+             <entry>ground speed, course and climb rates are valid</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <para>
+       Here are all of the valid GPS operational modes. Altus Metrum
+       products will only ever report 'N' (not valid), 'A'
+       (Autonomous) modes or 'E' (Estimated). The remaining modes
+       are either testing modes or require additional data.
+      </para>
+      <table frame='all'>
+       <title>GPS Mode</title>
+       <tgroup cols='3' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='value'/>
+         <colspec align='center' colwidth='3*' colname='name'/>
+         <colspec align='left' colwidth='7*' colname='description'/>
+         <thead>
+           <row>
+             <entry align='center'>Mode</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Decsription</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>N</entry>
+             <entry>Not Valid</entry>
+             <entry>All data are invalid</entry>
+           </row>
+           <row>
+             <entry>A</entry>
+             <entry>Autonomous mode</entry>
+             <entry>Data are derived from satellite data</entry>
+           </row>
+           <row>
+             <entry>D</entry>
+             <entry>Differential Mode</entry>
+             <entry>
+                 Data are augmented with differential data from a
+                 known ground station. The SkyTraq unit in TeleMetrum
+                 does not support this mode
+               </entry>
+           </row>
+           <row>
+             <entry>E</entry>
+             <entry>Estimated</entry>
+             <entry>
+                 Data are estimated using dead reckoning from the
+                 last known data
+               </entry>
+           </row>
+           <row>
+             <entry>M</entry>
+             <entry>Manual</entry>
+             <entry>Data were entered manually</entry>
+           </row>
+           <row>
+             <entry>S</entry>
+             <entry>Simulated</entry>
+             <entry>GPS receiver testing mode</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
+    <section>
+      <title>GPS Satellite Data</title>
+      <informaltable frame='none' label='' tocentry='0'>
+       <tgroup cols='2' align='center' colsep='1' rowsep='1'>
+         <colspec align='center' colwidth='*' colname='Offset'/>
+         <colspec align='left' colwidth='3*' colname='Description'/>
+         <thead>
+           <row>
+             <entry>Type</entry>
+             <entry>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0x06</entry>
+             <entry>GPS Satellite Data</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </informaltable>
+      <para>
+       This packet provides space vehicle identifiers and signal
+       quality information in the form of a C/N1 number for up to 12
+       satellites. The order of the svids is not specified.
+      </para>
+      <para>
+       GPS Satellite data are transmitted once per second during all
+       phases of the flight.
+      </para>
+      <table frame='all'>
+       <title>GPS Satellite Data Contents</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='right' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>5</entry><entry>uint8_t</entry><entry>channels</entry>
+             <entry>Number of reported satellite information</entry>
+           </row>
+           <row>
+             <entry>6</entry><entry>sat_info_t</entry><entry>sats[12]</entry>
+             <entry>See Per-Satellite data table below</entry>
+           </row>
+           <row>
+             <entry>30</entry><entry>uint8_t</entry><entry>unused[2]</entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <table frame='all'>
+       <title>GPS Per-Satellite data (sat_info_t)</title>
+       <tgroup cols='4' align='center' colsep='1' rowsep='1'>
+         <colspec align='right' colwidth='*' colname='Offset'/>
+         <colspec align='center' colwidth='3*' colname='Data Type'/>
+         <colspec align='left' colwidth='3*' colname='Name'/>
+         <colspec align='left' colwidth='9*' colname='Description'/>
+         <thead>
+           <row>
+             <entry align='center'>Offset</entry>
+             <entry align='center'>Data Type</entry>
+             <entry align='center'>Name</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>0</entry><entry>uint8_t</entry><entry>svid</entry>
+             <entry>Space Vehicle Identifier</entry>
+           </row>
+           <row>
+             <entry>1</entry><entry>uint8_t</entry><entry>c_n_1</entry>
+             <entry>C/N1 signal quality indicator</entry>
+           </row>
+           <row>
+             <entry>2</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
+  </section>
+  <section>
+    <title>Data Transmission</title>
+    <para>
+      Altus Metrum devices use the Texas Instruments CC1111
+      microcontroller which includes an integrated sub-GHz digital
+      transceiver. This transceiver is used to both transmit and
+      receive the telemetry packets. This section discusses what
+      modulation scheme is used and how this device is configured.
+    </para>
+    <section>
+      <title>Modulation Scheme</title>
+      <para>
+       Texas Instruments provides a tool for computing modulation
+       parameters given a desired modulation format and basic bit
+       rate. For AltOS, the basic bit rate was specified as 38 kBaud,
+       resulting in the following signal parmeters:
+      </para>
+      <table>
+       <tgroup cols='3'>
+         <colspec align="center" colwidth="*" colname="parameter"/>
+         <colspec align="center" text-align="." colwidth="*" colname="value"/>
+         <colspec align="center" colwidth="*" colname="description"/>
+         <thead>
+           <row>
+             <entry align='center'>Parameter</entry>
+             <entry align='center'>Value</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>Modulation</entry>
+             <entry>GFSK</entry>
+             <entry>Gaussian Frequency Shift Keying</entry>
+           </row>
+           <row>
+             <entry>Deviation</entry>
+             <entry>20.507812 kHz</entry>
+             <entry>Frequency modulation</entry>
+           </row>
+           <row>
+             <entry>Data rate</entry>
+             <entry>38.360596 kBaud</entry>
+             <entry>Raw bit rate</entry>
+           </row>
+           <row>
+             <entry>RX Filter Bandwidth</entry>
+             <entry>93.75 kHz</entry>
+             <entry>Receiver Band pass filter bandwidth</entry>
+           </row>
+           <row>
+             <entry>IF Frequency</entry>
+             <entry>140.62 kHz</entry>
+             <entry>Receiver intermediate frequency</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
+    <section>
+      <para>
+       The cc1111 provides forward error correction in hardware,
+       which AltOS uses to improve reception of weak signals. The
+       overall effect of this is to halve the available bandwidth for
+       data from 38 kBaud to 19 kBaud.
+      </para>
+      <title>Error Correction</title>
+      <table>
+       <tgroup cols='3'>
+         <colspec align="center" colwidth="*" colname="parameter"/>
+         <colspec align="center" colwidth="*" colname="value"/>
+         <colspec align="center" colwidth="*" colname="description"/>
+         <thead>
+           <row>
+             <entry align='center'>Parameter</entry>
+             <entry align='center'>Value</entry>
+             <entry align='center'>Description</entry>
+           </row>
+         </thead>
+         <tbody>
+           <row>
+             <entry>Error Correction</entry>
+             <entry>Convolutional coding FEC</entry>
+             <entry>1/2 code, constraint length m=4</entry>
+           </row>
+           <row>
+             <entry>Interleaving</entry>
+             <entry>4 x 4</entry>
+             <entry>Reduce effect of noise burst</entry>
+           </row>
+           <row>
+             <entry>Data Whitening</entry>
+             <entry>XOR with 9-bit PNR</entry>
+             <entry>Rotate right with bit 8 = bit 0 xor bit 5, initial
+             value 111111111</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
+  </section>
+  <section>
+    <title>TeleDongle packet format</title>
+    <para>
+      TeleDongle does not do any interpretation of the packet data,
+      instead it is configured to receive packets of a specified
+      length (32 bytes in this case). For each received packet,
+      TeleDongle produces a single line of text. This line starts with
+      the string "TELEM " and is followed by a list of hexadecimal
+      encoded bytes.
+    </para>
+    <programlisting>TELEM 224f01080b05765e00701f1a1bbeb8d7b60b070605140c000600000000000000003fa988</programlisting>
+    <para>
+      The hexadecimal encoded string of bytes contains a length byte,
+      the packet data, two bytes added by the cc1111 radio receiver
+      hardware and finally a checksum so that the host software can
+      validate that the line was transmitted without any errors.
+    </para>
+    <table>
+      <tgroup cols='4'>
+       <colspec align="center" colwidth="2*" colname="offset"/>
+       <colspec align="center" colwidth="*" colname="name"/>
+       <colspec align="center" colwidth="*" colname="value"/>
+       <colspec align="center" colwidth="5*" colname="description"/>
+       <thead>
+         <row>
+           <entry align='center'>Offset</entry>
+           <entry align='center'>Name</entry>
+           <entry align='center'>Example</entry>
+           <entry align='center'>Description</entry>
+         </row>
+       </thead>
+       <tbody>
+         <row>
+           <entry>0</entry>
+           <entry>length</entry>
+           <entry>22</entry>
+           <entry>Total length of data bytes in the line. Note that
+           this includes the added RSSI and status bytes</entry>
+         </row>
+         <row>
+           <entry>1 ·· length-3</entry>
+           <entry>packet</entry>
+           <entry>4f ·· 00</entry>
+           <entry>Bytes of actual packet data</entry>
+         </row>
+         <row>
+           <entry>length-2</entry>
+           <entry>rssi</entry>
+           <entry>3f</entry>
+           <entry>Received signal strength. dBm = rssi / 2 - 74</entry>
+         </row>
+         <row>
+           <entry>length-1</entry>
+           <entry>lqi</entry>
+           <entry>a9</entry>
+           <entry>Link Quality Indicator and CRC status. Bit 7
+           is set when the CRC is correct</entry>
+         </row>
+         <row>
+           <entry>length</entry>
+           <entry>checksum</entry>
+           <entry>88</entry>
+           <entry>(0x5a + sum(bytes 1 ·· length-1)) % 256</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
+  </section>
+  <section>
+    <title>History and Motivation</title>
+    <para>
+      The original AltoOS telemetry mechanism encoded everything
+      available piece of information on the TeleMetrum hardware into a
+      single unified packet. Initially, the packets contained very
+      little data—some raw sensor readings along with the current GPS
+      coordinates when a GPS receiver was connected. Over time, the
+      amount of data grew to include sensor calibration data, GPS
+      satellite information and a host of internal state information
+      designed to help diagnose flight failures in case of a loss of
+      the on-board flight data.
+    </para>
+    <para>
+      Because every packet contained all of the data, packets were
+      huge—95 bytes long. Much of the information was also specific to
+      the TeleMetrum hardware. With the introduction of the TeleMini
+      flight computer, most of the data contained in the telemetry
+      packets was unavailable. Initially, a shorter, but still
+      comprehensive packet was implemented. This required that the
+      ground station be pre-configured as to which kind of packet to
+      expect.
+    </para>
+    <para>
+      The development of several companion boards also made the
+      shortcomings evident—each companion board would want to include
+      telemetry data in the radio link; with the original design, the
+      packet would have to hold the new data as well, requiring
+      additional TeleMetrum and ground station changes.
+    </para>
+  </section>
+</article>
diff --git a/icon/altus-metrum-16x16.jpg b/icon/altus-metrum-16x16.jpg
new file mode 100644 (file)
index 0000000..8d8bbc6
Binary files /dev/null and b/icon/altus-metrum-16x16.jpg differ
diff --git a/icon/altus-metrum.ico b/icon/altus-metrum.ico
new file mode 100644 (file)
index 0000000..e32b4f1
Binary files /dev/null and b/icon/altus-metrum.ico differ
diff --git a/icon/grayled.png b/icon/grayled.png
new file mode 100644 (file)
index 0000000..bb6005c
Binary files /dev/null and b/icon/grayled.png differ
diff --git a/icon/grayon.png b/icon/grayon.png
new file mode 100644 (file)
index 0000000..c99b376
Binary files /dev/null and b/icon/grayon.png differ
diff --git a/icon/greenled.png b/icon/greenled.png
new file mode 100644 (file)
index 0000000..d766396
Binary files /dev/null and b/icon/greenled.png differ
diff --git a/icon/greenoff.png b/icon/greenoff.png
new file mode 100644 (file)
index 0000000..c3cf849
Binary files /dev/null and b/icon/greenoff.png differ
diff --git a/icon/redled.png b/icon/redled.png
new file mode 100644 (file)
index 0000000..230afae
Binary files /dev/null and b/icon/redled.png differ
diff --git a/icon/redoff.png b/icon/redoff.png
new file mode 100644 (file)
index 0000000..a251402
Binary files /dev/null and b/icon/redoff.png differ
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644 (file)
index 0000000..cae36ae
--- /dev/null
@@ -0,0 +1,3 @@
+altitude.h
+altitude-pa.h
+ao_whiten.h
diff --git a/src/25lc1024.h b/src/25lc1024.h
deleted file mode 100644 (file)
index 44e5238..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-/* Defines for the 25LC1024 1Mbit SPI Bus Serial EEPROM */
-
-#ifndef _25LC1024_H_
-#define _25LC1024_H_
-
-#define EE_READ                0x03
-#define EE_WRITE       0x02
-#define EE_WREN                0x06
-#define EE_WRDI                0x04
-#define EE_RDSR                0x05
-#define EE_WRSR                0x01
-#define EE_PE          0x42
-#define EE_SE          0xd8
-#define EE_CE          0xc7
-#define EE_RDID                0xab
-#define EE_DPD         0xb9
-
-#define EE_STATUS_WIP  (1 << 0)
-#define EE_STATUS_WEL  (1 << 1)
-#define EE_STATUS_BP0  (1 << 2)
-#define EE_STATUS_BP1  (1 << 3)
-#define EE_STATUS_WPEN (1 << 7)
-
-#endif /* _25LC1024_H_ */
index 5a8afe0b9a5859a55abd80e313973d307d06eefd..b8828d461d65e27e9c393b6163e035c48fbd39b7 100644 (file)
 #
 # AltOS build
 #
-#
-CC=sdcc
 
-ifndef VERSION
-VERSION=$(shell git describe)
+vpath make-altitude util
+vpath make-altitude-pa util
+vpath make-kalman util
+vpath make-whiten util
+vpath kalman.5c kalman
+vpath kalman_filter.5c kalman
+vpath load_csv.5c kalman
+vpath matrix.5c kalman
+
+include Version
+
+ifneq ($(shell which sdcc),)
+       SUBDIRS+=\
+       telemetrum-v1.2 telemetrum-v1.1 telemetrum-v1.0 \
+       teledongle-v0.2 teledongle-v0.1 \
+       telemini-v1.0 telenano-v0.1 \
+       telebt-v0.0 telebt-v0.1 \
+       telemetrum-v0.1-sky telemetrum-v0.1-sirf \
+       telelaunch-v0.1 tidongle test \
+       teleterra-v0.2 teleshield-v0.1 \
+       telefire-v0.1 \
+       spiradio-v0.1
 endif
 
-CFLAGS=--model-small --debug --opt-code-speed
-
-LDFLAGS=--out-fmt-ihx --code-loc 0x0000 --code-size 0x8000 \
-       --xram-loc 0xf000 --xram-size 0xda2 --iram-size 0xff
-
-INC = \
-       ao.h \
-       cc1111.h \
-       altitude.h \
-       25lc1024.h
-
-#
-# Common AltOS sources
-#
-ALTOS_SRC = \
-       ao_cmd.c \
-       ao_dbg.c \
-       ao_dma.c \
-       ao_mutex.c \
-       ao_panic.c \
-       ao_task.c \
-       ao_timer.c \
-       _bp.c
-
-#
-# Shared AltOS drivers
-#
-ALTOS_DRIVER_SRC = \
-       ao_beep.c \
-       ao_config.c \
-       ao_led.c \
-       ao_radio.c \
-       ao_stdio.c \
-       ao_usb.c
-
-TELE_COMMON_SRC = \
-       ao_gps_print.c \
-       ao_state.c
-
-#
-# Receiver code
-#
-TELE_RECEIVER_SRC =\
-       ao_monitor.c \
-       ao_rssi.c
-
-#
-# Shared Tele drivers (on TeleMetrum, TeleTerra, TeleDongle)
-#
-
-TELE_DRIVER_SRC = \
-       ao_convert.c \
-       ao_gps.c \
-       ao_serial.c
-
-#
-# Drivers for partially-flled boards (TT, TD and TI)
-#
-TELE_FAKE_SRC = \
-       ao_adc_fake.c \
-       ao_ee_fake.c
-
-#
-# Drivers only on TeleMetrum
-#
-TM_DRIVER_SRC = \
-       ao_adc.c \
-       ao_ee.c \
-       ao_gps_report.c \
-       ao_ignite.c
-
-#
-# Tasks run on TeleMetrum
-#
-TM_TASK_SRC = \
-       ao_flight.c \
-       ao_log.c \
-       ao_report.c \
-       ao_telemetry.c
-
-TM_MAIN_SRC = \
-       ao_telemetrum.c
-
-#
-# All sources for TeleMetrum
-#
-TM_SRC = \
-       $(ALTOS_SRC) \
-       $(ALTOS_DRIVER_SRC) \
-       $(TELE_DRIVER_SRC) \
-       $(TELE_COMMON_SRC) \
-       $(TM_DRIVER_SRC) \
-       $(TM_TASK_SRC) \
-       $(TM_MAIN_SRC)
-
-TI_MAIN_SRC = \
-       ao_tidongle.c
-
-#
-# All sources for the TI debug dongle
-#
-TI_SRC = \
-       $(ALTOS_SRC) \
-       $(ALTOS_DRIVER_SRC) \
-       $(TELE_RECEIVER_SRC) \
-       $(TELE_COMMON_SRC) \
-       $(TELE_FAKE_SRC) \
-       $(TI_MAIN_SRC)
-
-TT_MAIN_SRC = \
-       ao_teleterra.c
-#
-# All sources for TeleTerra
-#
-TT_SRC = \
-       $(ALTOS_SRC) \
-       $(ALTOS_DRIVER_SRC) \
-       $(TELE_RECEIVER_SRC) \
-       $(TELE_DRIVER_SRC) \
-       $(TELE_COMMON_SRC) \
-       $(TELE_FAKE_SRC) \
-       $(TT_MAIN_SRC)
-
-
-#
-# Sources for TeleDongle
-#
-
-TD_MAIN_SRC = \
-       ao_teledongle.c
-
-TD_SRC = \
-       $(ALTOS_SRC) \
-       $(ALTOS_DRIVER_SRC) \
-       $(TELE_RECEIVER_SRC) \
-       $(TELE_COMMON_SRC) \
-       $(TELE_FAKE_SRC) \
-       $(TD_MAIN_SRC)
-
-SRC = \
-       $(ALTOS_SRC) \
-       $(ALTOS_DRIVER_SRC) \
-       $(TELE_DRIVER_SRC) \
-       $(TELE_RECEIVER_SRC) \
-       $(TELE_COMMON_SRC) \
-       $(TELE_FAKE_SRC) \
-       $(TM_DRIVER_SRC) \
-       $(TM_TASK_SRC) \
-       $(TM_MAIN_SRC) \
-       $(TI_MAIN_SRC) \
-       $(TD_MAIN_SRC) \
-       $(TT_MAIN_SRC)
-
-TM_REL=$(TM_SRC:.c=.rel) ao_product-telemetrum.rel
-TI_REL=$(TI_SRC:.c=.rel) ao_product-tidongle.rel
-TT_REL=$(TT_SRC:.c=.rel) ao_product-teleterra.rel
-TD_REL=$(TD_SRC:.c=.rel) ao_product-teledongle.rel
-
-PROD_REL=\
-       ao_product-telemetrum.rel \
-       ao_product-tidongle.rel \
-       ao_product-teleterra.rel \
-       ao_product-teledongle.rel
-
-REL=$(SRC:.c=.rel) $(PROD_REL)
-ADB=$(REL:.rel=.adb)
-ASM=$(REL:.rel=.asm)
-LNK=$(REL:.rel=.lnk)
-LST=$(REL:.rel=.lst)
-RST=$(REL:.rel=.rst)
-SYM=$(REL:.rel=.sym)
-
-PROGS= telemetrum.ihx tidongle.ihx \
-       teleterra.ihx teledongle.ihx
-
-HOST_PROGS=ao_flight_test ao_gps_test
+ifneq ($(shell which avr-gcc),)
+       SUBDIRS += telescience-v0.1 telepyro-v0.1
+endif
 
-PCDB=$(PROGS:.ihx=.cdb)
-PLNK=$(PROGS:.ihx=.lnk)
-PMAP=$(PROGS:.ihx=.map)
-PMEM=$(PROGS:.ihx=.mem)
-PAOM=$(PROGS:.ihx=)
+ifneq ($(shell which arm-none-eabi-gcc),)
+       SUBDIRS += megametrum-v0.1 stm-bringup stm-demo telelco-v0.1
+endif
 
-%.rel : %.c $(INC)
-       $(CC) -c $(CFLAGS) -o$*.rel $*.c
+all: all-local all-recursive
 
-all: $(PROGS) $(HOST_PROGS)
+RECURSIVE_TARGETS = all-recursive clean-recursive install-recursive
 
-telemetrum.ihx: $(TM_REL) Makefile
-       $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(TM_REL)
-       sh check-stack ao.h telemetrum.mem
+$(RECURSIVE_TARGETS):
+       @target=`echo $@ | sed 's/-recursive//'`; \
+       for subdir in $(SUBDIRS); do \
+               echo "Making $$target in $$subdir"; \
+               (cd $$subdir && $(MAKE) $$target) || exit 1; \
+       done
 
-tidongle.ihx: $(TI_REL) Makefile
-       $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(TI_REL)
-       sh check-stack ao.h tidongle.mem
+distclean:     clean
 
-tidongle.ihx: telemetrum.ihx
+clean: clean-local clean-recursive
 
-teleterra.ihx: $(TT_REL) Makefile
-       $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(TT_REL)
-       sh check-stack ao.h teleterra.mem
+install: install-recursive
 
-teleterra.ihx: tidongle.ihx
+uninstall:
 
-teledongle.ihx: $(TD_REL) Makefile
-       $(CC) $(LDFLAGS) $(CFLAGS) -o $@ $(TD_REL)
-       sh check-stack ao.h teledongle.mem
+all-recursive: all-local
 
-teledongle.ihx: teleterra.ihx
+all-local: altitude.h altitude-pa.h ao_kalman.h ao_whiten.h
 
 altitude.h: make-altitude
-       nickle make-altitude > altitude.h
-
-TELEMETRUM_DEFS=ao-telemetrum.h
-TELETERRA_DEFS=ao-teleterra.h
-TELEDONGLE_DEFS=ao-teledongle.h
-TIDONGLE_DEFS=ao-tidongle.h
-
-ALL_DEFS=$(TELEMETRUM_DEFS) $(TELETERRA_DEFS) \
-       $(TELEDONGLE_DEFS) $(TIDONGLE_DEFS)
-ao_product-telemetrum.rel: ao_product.c $(TELEMETRUM_DEFS)
-       $(CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"$(TELEMETRUM_DEFS)\"' -o$@ ao_product.c
-
-ao_product-teleterra.rel: ao_product.c $(TELETERRA_DEFS)
-       $(CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"$(TELETERRA_DEFS)\"' -o$@ ao_product.c
-
-ao_product-teledongle.rel: ao_product.c $(TELEDONGLE_DEFS)
-       $(CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"$(TELEDONGLE_DEFS)\"' -o$@ ao_product.c
-
-ao_product-tidongle.rel: ao_product.c $(TIDONGLE_DEFS)
-       $(CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"$(TIDONGLE_DEFS)\"' -o$@ ao_product.c
-
-$(TELEMETRUM_DEFS): ao-make-product.5c
-       nickle ao-make-product.5c -m altusmetrum.org -p TeleMetrum -v $(VERSION) > $@
-
-$(TELETERRA_DEFS): ao-make-product.5c
-       nickle ao-make-product.5c -m altusmetrum.org -p TeleTerra -v $(VERSION) > $@
-
-$(TELEDONGLE_DEFS): ao-make-product.5c
-       nickle ao-make-product.5c -m altusmetrum.org -p TeleDongle -v $(VERSION) > $@
-
-$(TIDONGLE_DEFS): ao-make-product.5c
-       nickle ao-make-product.5c -m altusmetrum.org -p TIDongle -v $(VERSION) > $@
-
-distclean:     clean
+       nickle $< > $@
 
-clean:
-       rm -f $(ADB) $(ASM) $(LNK) $(LST) $(REL) $(RST) $(SYM)
-       rm -f $(PROGS) $(PCDB) $(PLNK) $(PMAP) $(PMEM) $(PAOM)
-       rm -f $(ALL_DEFS) $(HOST_PROGS)
-       rm -f $(TELEMETRUM_DEFS) $(TELETERRA_DEFS) $(TELEDONGLE_DEFS) $(TIDONGLE_DEFS)
+altitude-pa.h: make-altitude-pa
+       nickle $< > $@
 
-install:
+ao_kalman.h: make-kalman kalman.5c kalman_filter.5c load_csv.5c matrix.5c
+       bash $< kalman > $@
 
-ao_flight_test: ao_flight.c ao_flight_test.c
-       cc -g -o $@ ao_flight_test.c
+ao_whiten.h: make-whiten
+       nickle $< > $@
 
-ao_gps_test: ao_gps.c ao_gps_test.c ao_host.h
-       cc -g -o $@ ao_gps_test.c
+clean-local:
+       rm -f altitude.h ao_kalman.h
diff --git a/src/Version.in b/src/Version.in
new file mode 100644 (file)
index 0000000..aff9490
--- /dev/null
@@ -0,0 +1 @@
+VERSION=@VERSION@
diff --git a/src/_bp.c b/src/_bp.c
deleted file mode 100644 (file)
index 6bf135b..0000000
--- a/src/_bp.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*-------------------------------------------------------------------------
-
-  _bp.c :- just declares bp as a variable
-
-             Written By -  Sandeep Dutta . sandeep.dutta@usa.net (1999)
-
-   This library is free software; you can redistribute it and/or modify it
-   under the terms of the GNU Library General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any
-   later version.
-
-   This library 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 Library General Public License for more details.
-
-   You should have received a copy of the GNU Library General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-   In other words, you are welcome to use, share and improve this program.
-   You are forbidden to forbid anyone else to use, share and improve
-   what you give them.   Help stamp out software-hoarding!
--------------------------------------------------------------------------*/
-
-__data unsigned char bp ;
diff --git a/src/aes/ao_aes.c b/src/aes/ao_aes.c
new file mode 100644 (file)
index 0000000..4977aaf
--- /dev/null
@@ -0,0 +1,390 @@
+/* Copyright (C) 2000-2009 Peter Selinger.
+   This file is part of ccrypt. It is free software and it is covered
+   by the GNU general public license. See the file COPYING for details. */
+
+/* rijndael.c - optimized version of the Rijndeal cipher */
+/* $Id: rijndael.c 258 2009-08-26 17:46:10Z selinger $ */
+
+/* derived from original source: rijndael-alg-ref.c   v2.0   August '99
+ * Reference ANSI C code for NIST competition
+ * authors: Paulo Barreto
+ *          Vincent Rijmen
+ */
+
+#include <ao.h>
+#include <ao_aes.h>
+#include "ao_aes_int.h"
+
+static const int xshifts[3][2][4] = {
+  {{0, 1, 2, 3},
+   {0, 3, 2, 1}},
+
+  {{0, 1, 2, 3},
+   {0, 5, 4, 3}},
+
+  {{0, 1, 3, 4},
+   {0, 7, 5, 4}},
+};
+
+/* Exor corresponding text input and round key input bytes */
+/* the result is written to res, which can be the same as a */
+static inline void xKeyAddition(word32 res[MAXBC], word32 a[MAXBC],
+                        word32 rk[MAXBC], int BC)
+{
+  int j;
+
+  for (j = 0; j < BC; j++) {
+    res[j] = a[j] ^ rk[j];
+  }
+}
+
+#if 0                          /* code included for reference */
+
+/* shift rows a, return result in res. This avoids having to copy a
+   tmp array back to a. res must not be a. */
+static inline void xShiftRow(word32 res[MAXBC], word32 a[MAXBC], int shift[4],
+                     int BC)
+{
+  word8 (*a8)[4] = (word8 (*)[4]) a;
+  word8 (*res8)[4] = (word8 (*)[4]) res;
+
+  /* Row 0 remains unchanged
+   * The other three rows are shifted a variable amount
+   */
+  int i, j;
+  int s;
+
+  for (j = 0; j < BC; j++) {
+    res8[j][0] = a8[j][0];
+  }
+  for (i = 1; i < 4; i++) {
+    s = shift[i];
+    for (j = 0; j < BC; j++) {
+      res8[j][i] = a8[(j + s) % BC][i];
+    }
+  }
+}
+
+static inline void xSubstitution(word32 a[MAXBC], word8 box[256], int BC)
+{
+  word8 (*a8)[4] = (word8 (*)[4]) a;
+
+  /* Replace every byte of the input by the byte at that place
+   * in the nonlinear S-box
+   */
+  int i, j;
+
+  for (i = 0; i < 4; i++) {
+    for (j = 0; j < BC; j++) {
+      a8[j][i] = box[a[j][i]];
+    }
+  }
+}
+
+#endif                         /* code included for reference */
+
+/* profiling shows that the ccrypt program spends about 50% of its
+   time in the function xShiftSubst. Splitting the inner "for"
+   statement into two parts - versus using the expensive "%" modulo
+   operation, makes this function about 44% faster, thereby making the
+   entire program about 28% faster. With -O3 optimization, the time
+   savings are even more dramatic - ccrypt runs between 55% and 65%
+   faster on most platforms. */
+
+/* do ShiftRow and Substitution together. res must not be a. */
+static inline void xShiftSubst(word32 res[MAXBC], word32 a[MAXBC],
+                       int shift[4], int BC, const word8 box[256])
+{
+  int i, j;
+  int s;
+  word8 (*a8)[4] = (word8 (*)[4]) a;
+  word8 (*res8)[4] = (word8 (*)[4]) res;
+
+  for (j = 0; j < BC; j++) {
+    res8[j][0] = box[a8[j][0]];
+  }
+  for (i = 1; i < 4; i++) {
+    s = shift[i];
+    for (j = 0; j < BC - s; j++) {
+      res8[j][i] = box[a8[(j + s)][i]];
+    }
+    for (j = BC - s; j < BC; j++) {
+      res8[j][i] = box[a8[(j + s) - BC][i]];
+    }
+  }
+}
+
+#if 0                          /* code included for reference */
+
+/* Mix the four bytes of every column in a linear way */
+/* the result is written to res, which may equal a */
+static inline void xMixColumn(word32 res[MAXBC], word32 a[MAXBC], int BC)
+{
+  int j;
+  word32 b;
+  word8 (*a8)[4] = (word8 (*)[4]) a;
+
+  for (j = 0; j < BC; j++) {
+    b = M0[0][a8[j][0]].w32;
+    b ^= M0[1][a8[j][1]].w32;
+    b ^= M0[2][a8[j][2]].w32;
+    b ^= M0[3][a8[j][3]].w32;
+    res[j] = b;
+  }
+}
+
+#endif                         /* code included for reference */
+
+/* do MixColumn and KeyAddition together */
+static inline void xMixAdd(word32 res[MAXBC], word32 a[MAXBC],
+                   word32 rk[MAXBC], int BC)
+{
+  int j;
+  word32 b;
+  word8 (*a8)[4] = (word8 (*)[4]) a;
+
+  for (j = 0; j < BC; j++) {
+    b = M0[0][a8[j][0]].w32;
+    b ^= M0[1][a8[j][1]].w32;
+    b ^= M0[2][a8[j][2]].w32;
+    b ^= M0[3][a8[j][3]].w32;
+    b ^= rk[j];
+    res[j] = b;
+  }
+}
+
+/* Mix the four bytes of every column in a linear way
+ * This is the opposite operation of xMixColumn */
+/* the result is written to res, which may equal a */
+static inline void xInvMixColumn(word32 res[MAXBC], word32 a[MAXBC], int BC)
+{
+  int j;
+  word32 b;
+  word8 (*a8)[4] = (word8 (*)[4]) a;
+
+  for (j = 0; j < BC; j++) {
+    b = M1[0][a8[j][0]].w32;
+    b ^= M1[1][a8[j][1]].w32;
+    b ^= M1[2][a8[j][2]].w32;
+    b ^= M1[3][a8[j][3]].w32;
+    res[j] = b;
+  }
+}
+
+#if 0                          /* code included for reference */
+
+/* do KeyAddition and InvMixColumn together */
+static inline void xAddInvMix(word32 res[MAXBC], word32 a[MAXBC],
+                      word32 rk[MAXBC], int BC)
+{
+  int j;
+  word32 b;
+  word8 (*a8)[4] = (word8 (*)[4]) a;
+
+  for (j = 0; j < BC; j++) {
+    a[j] = a[j] ^ rk[j];
+    b = M1[0][a8[j][0]].w32;
+    b ^= M1[1][a8[j][1]].w32;
+    b ^= M1[2][a8[j][2]].w32;
+    b ^= M1[3][a8[j][3]].w32;
+    res[j] = b;
+  }
+}
+
+#endif                         /* code included for reference */
+
+int xrijndaelKeySched(word32 key[], int keyBits, int blockBits,
+                     roundkey *rkk)
+{
+  /* Calculate the necessary round keys
+   * The number of calculations depends on keyBits and blockBits */
+  int KC, BC, ROUNDS;
+  int i, j, t, rconpointer = 0;
+  word8 (*k8)[4] = (word8 (*)[4]) key;
+
+  switch (keyBits) {
+  case 128:
+    KC = 4;
+    break;
+  case 192:
+    KC = 6;
+    break;
+  case 256:
+    KC = 8;
+    break;
+  default:
+    return -1;
+  }
+
+  switch (blockBits) {
+  case 128:
+    BC = 4;
+    break;
+  case 192:
+    BC = 6;
+    break;
+  case 256:
+    BC = 8;
+    break;
+  default:
+    return -2;
+  }
+
+  ROUNDS = KC > BC ? KC + 6 : BC + 6;
+
+  t = 0;
+  /* copy values into round key array */
+  for (j = 0; (j < KC) && (t < (ROUNDS + 1) * BC); j++, t++)
+    rkk->rk[t] = key[j];
+
+  while (t < (ROUNDS + 1) * BC) {  /* while not enough round key material */
+    /* calculate new values */
+    for (i = 0; i < 4; i++) {
+      k8[0][i] ^= xS[k8[KC - 1][(i + 1) % 4]];
+    }
+    k8[0][0] ^= xrcon[rconpointer++];
+
+    if (KC != 8) {
+      for (j = 1; j < KC; j++) {
+       key[j] ^= key[j - 1];
+      }
+    } else {
+      for (j = 1; j < 4; j++) {
+       key[j] ^= key[j - 1];
+      }
+      for (i = 0; i < 4; i++) {
+       k8[4][i] ^= xS[k8[3][i]];
+      }
+      for (j = 5; j < 8; j++) {
+       key[j] ^= key[j - 1];
+      }
+    }
+    /* copy values into round key array */
+    for (j = 0; (j < KC) && (t < (ROUNDS + 1) * BC); j++, t++) {
+      rkk->rk[t] = key[j];
+    }
+  }
+
+  /* make roundkey structure */
+  rkk->BC = BC;
+  rkk->KC = KC;
+  rkk->ROUNDS = ROUNDS;
+  for (i = 0; i < 2; i++) {
+    for (j = 0; j < 4; j++) {
+      rkk->shift[i][j] = xshifts[(BC - 4) >> 1][i][j];
+    }
+  }
+
+  return 0;
+}
+
+/* Encryption of one block. */
+
+void xrijndaelEncrypt(word32 block[], roundkey *rkk)
+{
+  word32 block2[MAXBC];                /* hold intermediate result */
+  int r;
+
+  int *shift = rkk->shift[0];
+  int BC = rkk->BC;
+  int ROUNDS = rkk->ROUNDS;
+  word32 *rp = rkk->rk;
+
+  /* begin with a key addition */
+  xKeyAddition(block, block, rp, BC);
+  rp += BC;
+
+  /* ROUNDS-1 ordinary rounds */
+  for (r = 1; r < ROUNDS; r++) {
+    xShiftSubst(block2, block, shift, BC, xS);
+    xMixAdd(block, block2, rp, BC);
+    rp += BC;
+  }
+
+  /* Last round is special: there is no xMixColumn */
+  xShiftSubst(block2, block, shift, BC, xS);
+  xKeyAddition(block, block2, rp, BC);
+}
+
+void xrijndaelDecrypt(word32 block[], roundkey *rkk)
+{
+  word32 block2[MAXBC];                /* hold intermediate result */
+  int r;
+
+  int *shift = rkk->shift[1];
+  int BC = rkk->BC;
+  int ROUNDS = rkk->ROUNDS;
+  word32 *rp = rkk->rk + ROUNDS * BC;
+
+  /* To decrypt: apply the inverse operations of the encrypt routine,
+   *             in opposite order
+   * 
+   * (xKeyAddition is an involution: it's equal to its inverse)
+   * (the inverse of xSubstitution with table S is xSubstitution with the 
+   * inverse table of S)
+   * (the inverse of xShiftRow is xShiftRow over a suitable distance)
+   */
+
+  /* First the special round:
+   *   without xInvMixColumn
+   *   with extra xKeyAddition
+   */
+  xKeyAddition(block2, block, rp, BC);
+  xShiftSubst(block, block2, shift, BC, xSi);
+  rp -= BC;
+
+  /* ROUNDS-1 ordinary rounds
+   */
+  for (r = ROUNDS - 1; r > 0; r--) {
+    xKeyAddition(block, block, rp, BC);
+    xInvMixColumn(block2, block, BC);
+    xShiftSubst(block, block2, shift, BC, xSi);
+    rp -= BC;
+  }
+
+  /* End with the extra key addition
+   */
+
+  xKeyAddition(block, block, rp, BC);
+}
+
+uint8_t ao_aes_mutex;
+static roundkey        rkk;
+
+static uint8_t iv[16];
+
+void
+ao_aes_set_mode(enum ao_aes_mode mode)
+{
+       /* we only do CBC_MAC anyways... */
+}
+
+void
+ao_aes_set_key(__xdata uint8_t *in)
+{
+       xrijndaelKeySched((word32 *) in, 128, 128, &rkk);
+}
+
+void
+ao_aes_zero_iv(void)
+{
+       memset(iv, '\0', sizeof (iv));
+}
+
+void
+ao_aes_run(__xdata uint8_t *in,
+          __xdata uint8_t *out)
+{
+       uint8_t i;
+
+       for (i = 0; i < 16; i++)
+               iv[i] ^= in[i];
+       xrijndaelEncrypt((word32 *) iv, &rkk);
+       if (out)
+               memcpy(out, iv, 16);
+}
+
+void
+ao_aes_init(void)
+{
+}
diff --git a/src/aes/ao_aes_int.h b/src/aes/ao_aes_int.h
new file mode 100644 (file)
index 0000000..7990a2e
--- /dev/null
@@ -0,0 +1,64 @@
+/* Copyright (C) 2000-2009 Peter Selinger.
+   This file is part of ccrypt. It is free software and it is covered
+   by the GNU general public license. See the file COPYING for details. */
+
+/* rijndael.h */
+/* $Id: rijndael.h 258 2009-08-26 17:46:10Z selinger $ */
+
+/* derived from original source: rijndael-alg-ref.h   v2.0   August '99
+ * Reference ANSI C code for NIST competition
+ * authors: Paulo Barreto
+ *          Vincent Rijmen
+ */
+
+#ifndef __RIJNDAEL_H
+#define __RIJNDAEL_H
+
+#include <stdint.h>
+
+typedef uint8_t word8;
+typedef uint32_t word32;
+
+/* a type to hold 32 bits accessible as 1 integer or 4 bytes */
+union word8x4_u {
+  word8 w8[4];
+  word32 w32;
+};
+typedef union word8x4_u word8x4;
+
+#include "ao_aes_tables.h"
+
+#define MAXBC          (256/32)
+#define MAXKC          (256/32)
+#define MAXROUNDS      14
+#define MAXRK           ((MAXROUNDS+1)*MAXBC)
+
+typedef struct {
+  int BC;
+  int KC;
+  int ROUNDS;
+  int shift[2][4];
+  word32 rk[MAXRK];
+} roundkey;
+
+/* keys and blocks are externally treated as word32 arrays, to
+   make sure they are aligned on 4-byte boundaries on architectures
+   that require it. */
+
+/* make a roundkey rkk from key. key must have appropriate size given
+   by keyBits. keyBits and blockBits may only be 128, 196, or
+   256. Returns non-zero if arguments are invalid. */
+
+int xrijndaelKeySched(word32 key[], int keyBits, int blockBits,
+                     roundkey *rkk);
+
+/* encrypt, resp. decrypt, block using rijndael roundkey rkk. rkk must
+   have been created with xrijndaelKeySched. Size of block, in bits,
+   must be equal to blockBits parameter that was used to make rkk. In
+   all other cases, behavior is undefined - for reasons of speed, no
+   check for error conditions is done. */
+
+void xrijndaelEncrypt(word32 block[], roundkey *rkk);
+void xrijndaelDecrypt(word32 block[], roundkey *rkk);
+
+#endif                         /* __RIJNDAEL_H */
diff --git a/src/aes/ao_aes_tables.c b/src/aes/ao_aes_tables.c
new file mode 100644 (file)
index 0000000..1bca227
--- /dev/null
@@ -0,0 +1,768 @@
+/* Copyright (C) 2000-2009 Peter Selinger.
+   This file is part of ccrypt. It is free software and it is covered
+   by the GNU general public license. See the file COPYING for details. */
+
+/* generated by maketables.c */
+
+#include "ao_aes_int.h"
+
+const word8x4 M0[4][256] = {
+ {
+  {{  0,   0,   0,   0}}, {{  2,   1,   1,   3}}, {{  4,   2,   2,   6}}, 
+  {{  6,   3,   3,   5}}, {{  8,   4,   4,  12}}, {{ 10,   5,   5,  15}}, 
+  {{ 12,   6,   6,  10}}, {{ 14,   7,   7,   9}}, {{ 16,   8,   8,  24}}, 
+  {{ 18,   9,   9,  27}}, {{ 20,  10,  10,  30}}, {{ 22,  11,  11,  29}}, 
+  {{ 24,  12,  12,  20}}, {{ 26,  13,  13,  23}}, {{ 28,  14,  14,  18}}, 
+  {{ 30,  15,  15,  17}}, {{ 32,  16,  16,  48}}, {{ 34,  17,  17,  51}}, 
+  {{ 36,  18,  18,  54}}, {{ 38,  19,  19,  53}}, {{ 40,  20,  20,  60}}, 
+  {{ 42,  21,  21,  63}}, {{ 44,  22,  22,  58}}, {{ 46,  23,  23,  57}}, 
+  {{ 48,  24,  24,  40}}, {{ 50,  25,  25,  43}}, {{ 52,  26,  26,  46}}, 
+  {{ 54,  27,  27,  45}}, {{ 56,  28,  28,  36}}, {{ 58,  29,  29,  39}}, 
+  {{ 60,  30,  30,  34}}, {{ 62,  31,  31,  33}}, {{ 64,  32,  32,  96}}, 
+  {{ 66,  33,  33,  99}}, {{ 68,  34,  34, 102}}, {{ 70,  35,  35, 101}}, 
+  {{ 72,  36,  36, 108}}, {{ 74,  37,  37, 111}}, {{ 76,  38,  38, 106}}, 
+  {{ 78,  39,  39, 105}}, {{ 80,  40,  40, 120}}, {{ 82,  41,  41, 123}}, 
+  {{ 84,  42,  42, 126}}, {{ 86,  43,  43, 125}}, {{ 88,  44,  44, 116}}, 
+  {{ 90,  45,  45, 119}}, {{ 92,  46,  46, 114}}, {{ 94,  47,  47, 113}}, 
+  {{ 96,  48,  48,  80}}, {{ 98,  49,  49,  83}}, {{100,  50,  50,  86}}, 
+  {{102,  51,  51,  85}}, {{104,  52,  52,  92}}, {{106,  53,  53,  95}}, 
+  {{108,  54,  54,  90}}, {{110,  55,  55,  89}}, {{112,  56,  56,  72}}, 
+  {{114,  57,  57,  75}}, {{116,  58,  58,  78}}, {{118,  59,  59,  77}}, 
+  {{120,  60,  60,  68}}, {{122,  61,  61,  71}}, {{124,  62,  62,  66}}, 
+  {{126,  63,  63,  65}}, {{128,  64,  64, 192}}, {{130,  65,  65, 195}}, 
+  {{132,  66,  66, 198}}, {{134,  67,  67, 197}}, {{136,  68,  68, 204}}, 
+  {{138,  69,  69, 207}}, {{140,  70,  70, 202}}, {{142,  71,  71, 201}}, 
+  {{144,  72,  72, 216}}, {{146,  73,  73, 219}}, {{148,  74,  74, 222}}, 
+  {{150,  75,  75, 221}}, {{152,  76,  76, 212}}, {{154,  77,  77, 215}}, 
+  {{156,  78,  78, 210}}, {{158,  79,  79, 209}}, {{160,  80,  80, 240}}, 
+  {{162,  81,  81, 243}}, {{164,  82,  82, 246}}, {{166,  83,  83, 245}}, 
+  {{168,  84,  84, 252}}, {{170,  85,  85, 255}}, {{172,  86,  86, 250}}, 
+  {{174,  87,  87, 249}}, {{176,  88,  88, 232}}, {{178,  89,  89, 235}}, 
+  {{180,  90,  90, 238}}, {{182,  91,  91, 237}}, {{184,  92,  92, 228}}, 
+  {{186,  93,  93, 231}}, {{188,  94,  94, 226}}, {{190,  95,  95, 225}}, 
+  {{192,  96,  96, 160}}, {{194,  97,  97, 163}}, {{196,  98,  98, 166}}, 
+  {{198,  99,  99, 165}}, {{200, 100, 100, 172}}, {{202, 101, 101, 175}}, 
+  {{204, 102, 102, 170}}, {{206, 103, 103, 169}}, {{208, 104, 104, 184}}, 
+  {{210, 105, 105, 187}}, {{212, 106, 106, 190}}, {{214, 107, 107, 189}}, 
+  {{216, 108, 108, 180}}, {{218, 109, 109, 183}}, {{220, 110, 110, 178}}, 
+  {{222, 111, 111, 177}}, {{224, 112, 112, 144}}, {{226, 113, 113, 147}}, 
+  {{228, 114, 114, 150}}, {{230, 115, 115, 149}}, {{232, 116, 116, 156}}, 
+  {{234, 117, 117, 159}}, {{236, 118, 118, 154}}, {{238, 119, 119, 153}}, 
+  {{240, 120, 120, 136}}, {{242, 121, 121, 139}}, {{244, 122, 122, 142}}, 
+  {{246, 123, 123, 141}}, {{248, 124, 124, 132}}, {{250, 125, 125, 135}}, 
+  {{252, 126, 126, 130}}, {{254, 127, 127, 129}}, {{ 27, 128, 128, 155}}, 
+  {{ 25, 129, 129, 152}}, {{ 31, 130, 130, 157}}, {{ 29, 131, 131, 158}}, 
+  {{ 19, 132, 132, 151}}, {{ 17, 133, 133, 148}}, {{ 23, 134, 134, 145}}, 
+  {{ 21, 135, 135, 146}}, {{ 11, 136, 136, 131}}, {{  9, 137, 137, 128}}, 
+  {{ 15, 138, 138, 133}}, {{ 13, 139, 139, 134}}, {{  3, 140, 140, 143}}, 
+  {{  1, 141, 141, 140}}, {{  7, 142, 142, 137}}, {{  5, 143, 143, 138}}, 
+  {{ 59, 144, 144, 171}}, {{ 57, 145, 145, 168}}, {{ 63, 146, 146, 173}}, 
+  {{ 61, 147, 147, 174}}, {{ 51, 148, 148, 167}}, {{ 49, 149, 149, 164}}, 
+  {{ 55, 150, 150, 161}}, {{ 53, 151, 151, 162}}, {{ 43, 152, 152, 179}}, 
+  {{ 41, 153, 153, 176}}, {{ 47, 154, 154, 181}}, {{ 45, 155, 155, 182}}, 
+  {{ 35, 156, 156, 191}}, {{ 33, 157, 157, 188}}, {{ 39, 158, 158, 185}}, 
+  {{ 37, 159, 159, 186}}, {{ 91, 160, 160, 251}}, {{ 89, 161, 161, 248}}, 
+  {{ 95, 162, 162, 253}}, {{ 93, 163, 163, 254}}, {{ 83, 164, 164, 247}}, 
+  {{ 81, 165, 165, 244}}, {{ 87, 166, 166, 241}}, {{ 85, 167, 167, 242}}, 
+  {{ 75, 168, 168, 227}}, {{ 73, 169, 169, 224}}, {{ 79, 170, 170, 229}}, 
+  {{ 77, 171, 171, 230}}, {{ 67, 172, 172, 239}}, {{ 65, 173, 173, 236}}, 
+  {{ 71, 174, 174, 233}}, {{ 69, 175, 175, 234}}, {{123, 176, 176, 203}}, 
+  {{121, 177, 177, 200}}, {{127, 178, 178, 205}}, {{125, 179, 179, 206}}, 
+  {{115, 180, 180, 199}}, {{113, 181, 181, 196}}, {{119, 182, 182, 193}}, 
+  {{117, 183, 183, 194}}, {{107, 184, 184, 211}}, {{105, 185, 185, 208}}, 
+  {{111, 186, 186, 213}}, {{109, 187, 187, 214}}, {{ 99, 188, 188, 223}}, 
+  {{ 97, 189, 189, 220}}, {{103, 190, 190, 217}}, {{101, 191, 191, 218}}, 
+  {{155, 192, 192,  91}}, {{153, 193, 193,  88}}, {{159, 194, 194,  93}}, 
+  {{157, 195, 195,  94}}, {{147, 196, 196,  87}}, {{145, 197, 197,  84}}, 
+  {{151, 198, 198,  81}}, {{149, 199, 199,  82}}, {{139, 200, 200,  67}}, 
+  {{137, 201, 201,  64}}, {{143, 202, 202,  69}}, {{141, 203, 203,  70}}, 
+  {{131, 204, 204,  79}}, {{129, 205, 205,  76}}, {{135, 206, 206,  73}}, 
+  {{133, 207, 207,  74}}, {{187, 208, 208, 107}}, {{185, 209, 209, 104}}, 
+  {{191, 210, 210, 109}}, {{189, 211, 211, 110}}, {{179, 212, 212, 103}}, 
+  {{177, 213, 213, 100}}, {{183, 214, 214,  97}}, {{181, 215, 215,  98}}, 
+  {{171, 216, 216, 115}}, {{169, 217, 217, 112}}, {{175, 218, 218, 117}}, 
+  {{173, 219, 219, 118}}, {{163, 220, 220, 127}}, {{161, 221, 221, 124}}, 
+  {{167, 222, 222, 121}}, {{165, 223, 223, 122}}, {{219, 224, 224,  59}}, 
+  {{217, 225, 225,  56}}, {{223, 226, 226,  61}}, {{221, 227, 227,  62}}, 
+  {{211, 228, 228,  55}}, {{209, 229, 229,  52}}, {{215, 230, 230,  49}}, 
+  {{213, 231, 231,  50}}, {{203, 232, 232,  35}}, {{201, 233, 233,  32}}, 
+  {{207, 234, 234,  37}}, {{205, 235, 235,  38}}, {{195, 236, 236,  47}}, 
+  {{193, 237, 237,  44}}, {{199, 238, 238,  41}}, {{197, 239, 239,  42}}, 
+  {{251, 240, 240,  11}}, {{249, 241, 241,   8}}, {{255, 242, 242,  13}}, 
+  {{253, 243, 243,  14}}, {{243, 244, 244,   7}}, {{241, 245, 245,   4}}, 
+  {{247, 246, 246,   1}}, {{245, 247, 247,   2}}, {{235, 248, 248,  19}}, 
+  {{233, 249, 249,  16}}, {{239, 250, 250,  21}}, {{237, 251, 251,  22}}, 
+  {{227, 252, 252,  31}}, {{225, 253, 253,  28}}, {{231, 254, 254,  25}}, 
+  {{229, 255, 255,  26}}, 
+ },
+ {
+  {{  0,   0,   0,   0}}, {{  3,   2,   1,   1}}, {{  6,   4,   2,   2}}, 
+  {{  5,   6,   3,   3}}, {{ 12,   8,   4,   4}}, {{ 15,  10,   5,   5}}, 
+  {{ 10,  12,   6,   6}}, {{  9,  14,   7,   7}}, {{ 24,  16,   8,   8}}, 
+  {{ 27,  18,   9,   9}}, {{ 30,  20,  10,  10}}, {{ 29,  22,  11,  11}}, 
+  {{ 20,  24,  12,  12}}, {{ 23,  26,  13,  13}}, {{ 18,  28,  14,  14}}, 
+  {{ 17,  30,  15,  15}}, {{ 48,  32,  16,  16}}, {{ 51,  34,  17,  17}}, 
+  {{ 54,  36,  18,  18}}, {{ 53,  38,  19,  19}}, {{ 60,  40,  20,  20}}, 
+  {{ 63,  42,  21,  21}}, {{ 58,  44,  22,  22}}, {{ 57,  46,  23,  23}}, 
+  {{ 40,  48,  24,  24}}, {{ 43,  50,  25,  25}}, {{ 46,  52,  26,  26}}, 
+  {{ 45,  54,  27,  27}}, {{ 36,  56,  28,  28}}, {{ 39,  58,  29,  29}}, 
+  {{ 34,  60,  30,  30}}, {{ 33,  62,  31,  31}}, {{ 96,  64,  32,  32}}, 
+  {{ 99,  66,  33,  33}}, {{102,  68,  34,  34}}, {{101,  70,  35,  35}}, 
+  {{108,  72,  36,  36}}, {{111,  74,  37,  37}}, {{106,  76,  38,  38}}, 
+  {{105,  78,  39,  39}}, {{120,  80,  40,  40}}, {{123,  82,  41,  41}}, 
+  {{126,  84,  42,  42}}, {{125,  86,  43,  43}}, {{116,  88,  44,  44}}, 
+  {{119,  90,  45,  45}}, {{114,  92,  46,  46}}, {{113,  94,  47,  47}}, 
+  {{ 80,  96,  48,  48}}, {{ 83,  98,  49,  49}}, {{ 86, 100,  50,  50}}, 
+  {{ 85, 102,  51,  51}}, {{ 92, 104,  52,  52}}, {{ 95, 106,  53,  53}}, 
+  {{ 90, 108,  54,  54}}, {{ 89, 110,  55,  55}}, {{ 72, 112,  56,  56}}, 
+  {{ 75, 114,  57,  57}}, {{ 78, 116,  58,  58}}, {{ 77, 118,  59,  59}}, 
+  {{ 68, 120,  60,  60}}, {{ 71, 122,  61,  61}}, {{ 66, 124,  62,  62}}, 
+  {{ 65, 126,  63,  63}}, {{192, 128,  64,  64}}, {{195, 130,  65,  65}}, 
+  {{198, 132,  66,  66}}, {{197, 134,  67,  67}}, {{204, 136,  68,  68}}, 
+  {{207, 138,  69,  69}}, {{202, 140,  70,  70}}, {{201, 142,  71,  71}}, 
+  {{216, 144,  72,  72}}, {{219, 146,  73,  73}}, {{222, 148,  74,  74}}, 
+  {{221, 150,  75,  75}}, {{212, 152,  76,  76}}, {{215, 154,  77,  77}}, 
+  {{210, 156,  78,  78}}, {{209, 158,  79,  79}}, {{240, 160,  80,  80}}, 
+  {{243, 162,  81,  81}}, {{246, 164,  82,  82}}, {{245, 166,  83,  83}}, 
+  {{252, 168,  84,  84}}, {{255, 170,  85,  85}}, {{250, 172,  86,  86}}, 
+  {{249, 174,  87,  87}}, {{232, 176,  88,  88}}, {{235, 178,  89,  89}}, 
+  {{238, 180,  90,  90}}, {{237, 182,  91,  91}}, {{228, 184,  92,  92}}, 
+  {{231, 186,  93,  93}}, {{226, 188,  94,  94}}, {{225, 190,  95,  95}}, 
+  {{160, 192,  96,  96}}, {{163, 194,  97,  97}}, {{166, 196,  98,  98}}, 
+  {{165, 198,  99,  99}}, {{172, 200, 100, 100}}, {{175, 202, 101, 101}}, 
+  {{170, 204, 102, 102}}, {{169, 206, 103, 103}}, {{184, 208, 104, 104}}, 
+  {{187, 210, 105, 105}}, {{190, 212, 106, 106}}, {{189, 214, 107, 107}}, 
+  {{180, 216, 108, 108}}, {{183, 218, 109, 109}}, {{178, 220, 110, 110}}, 
+  {{177, 222, 111, 111}}, {{144, 224, 112, 112}}, {{147, 226, 113, 113}}, 
+  {{150, 228, 114, 114}}, {{149, 230, 115, 115}}, {{156, 232, 116, 116}}, 
+  {{159, 234, 117, 117}}, {{154, 236, 118, 118}}, {{153, 238, 119, 119}}, 
+  {{136, 240, 120, 120}}, {{139, 242, 121, 121}}, {{142, 244, 122, 122}}, 
+  {{141, 246, 123, 123}}, {{132, 248, 124, 124}}, {{135, 250, 125, 125}}, 
+  {{130, 252, 126, 126}}, {{129, 254, 127, 127}}, {{155,  27, 128, 128}}, 
+  {{152,  25, 129, 129}}, {{157,  31, 130, 130}}, {{158,  29, 131, 131}}, 
+  {{151,  19, 132, 132}}, {{148,  17, 133, 133}}, {{145,  23, 134, 134}}, 
+  {{146,  21, 135, 135}}, {{131,  11, 136, 136}}, {{128,   9, 137, 137}}, 
+  {{133,  15, 138, 138}}, {{134,  13, 139, 139}}, {{143,   3, 140, 140}}, 
+  {{140,   1, 141, 141}}, {{137,   7, 142, 142}}, {{138,   5, 143, 143}}, 
+  {{171,  59, 144, 144}}, {{168,  57, 145, 145}}, {{173,  63, 146, 146}}, 
+  {{174,  61, 147, 147}}, {{167,  51, 148, 148}}, {{164,  49, 149, 149}}, 
+  {{161,  55, 150, 150}}, {{162,  53, 151, 151}}, {{179,  43, 152, 152}}, 
+  {{176,  41, 153, 153}}, {{181,  47, 154, 154}}, {{182,  45, 155, 155}}, 
+  {{191,  35, 156, 156}}, {{188,  33, 157, 157}}, {{185,  39, 158, 158}}, 
+  {{186,  37, 159, 159}}, {{251,  91, 160, 160}}, {{248,  89, 161, 161}}, 
+  {{253,  95, 162, 162}}, {{254,  93, 163, 163}}, {{247,  83, 164, 164}}, 
+  {{244,  81, 165, 165}}, {{241,  87, 166, 166}}, {{242,  85, 167, 167}}, 
+  {{227,  75, 168, 168}}, {{224,  73, 169, 169}}, {{229,  79, 170, 170}}, 
+  {{230,  77, 171, 171}}, {{239,  67, 172, 172}}, {{236,  65, 173, 173}}, 
+  {{233,  71, 174, 174}}, {{234,  69, 175, 175}}, {{203, 123, 176, 176}}, 
+  {{200, 121, 177, 177}}, {{205, 127, 178, 178}}, {{206, 125, 179, 179}}, 
+  {{199, 115, 180, 180}}, {{196, 113, 181, 181}}, {{193, 119, 182, 182}}, 
+  {{194, 117, 183, 183}}, {{211, 107, 184, 184}}, {{208, 105, 185, 185}}, 
+  {{213, 111, 186, 186}}, {{214, 109, 187, 187}}, {{223,  99, 188, 188}}, 
+  {{220,  97, 189, 189}}, {{217, 103, 190, 190}}, {{218, 101, 191, 191}}, 
+  {{ 91, 155, 192, 192}}, {{ 88, 153, 193, 193}}, {{ 93, 159, 194, 194}}, 
+  {{ 94, 157, 195, 195}}, {{ 87, 147, 196, 196}}, {{ 84, 145, 197, 197}}, 
+  {{ 81, 151, 198, 198}}, {{ 82, 149, 199, 199}}, {{ 67, 139, 200, 200}}, 
+  {{ 64, 137, 201, 201}}, {{ 69, 143, 202, 202}}, {{ 70, 141, 203, 203}}, 
+  {{ 79, 131, 204, 204}}, {{ 76, 129, 205, 205}}, {{ 73, 135, 206, 206}}, 
+  {{ 74, 133, 207, 207}}, {{107, 187, 208, 208}}, {{104, 185, 209, 209}}, 
+  {{109, 191, 210, 210}}, {{110, 189, 211, 211}}, {{103, 179, 212, 212}}, 
+  {{100, 177, 213, 213}}, {{ 97, 183, 214, 214}}, {{ 98, 181, 215, 215}}, 
+  {{115, 171, 216, 216}}, {{112, 169, 217, 217}}, {{117, 175, 218, 218}}, 
+  {{118, 173, 219, 219}}, {{127, 163, 220, 220}}, {{124, 161, 221, 221}}, 
+  {{121, 167, 222, 222}}, {{122, 165, 223, 223}}, {{ 59, 219, 224, 224}}, 
+  {{ 56, 217, 225, 225}}, {{ 61, 223, 226, 226}}, {{ 62, 221, 227, 227}}, 
+  {{ 55, 211, 228, 228}}, {{ 52, 209, 229, 229}}, {{ 49, 215, 230, 230}}, 
+  {{ 50, 213, 231, 231}}, {{ 35, 203, 232, 232}}, {{ 32, 201, 233, 233}}, 
+  {{ 37, 207, 234, 234}}, {{ 38, 205, 235, 235}}, {{ 47, 195, 236, 236}}, 
+  {{ 44, 193, 237, 237}}, {{ 41, 199, 238, 238}}, {{ 42, 197, 239, 239}}, 
+  {{ 11, 251, 240, 240}}, {{  8, 249, 241, 241}}, {{ 13, 255, 242, 242}}, 
+  {{ 14, 253, 243, 243}}, {{  7, 243, 244, 244}}, {{  4, 241, 245, 245}}, 
+  {{  1, 247, 246, 246}}, {{  2, 245, 247, 247}}, {{ 19, 235, 248, 248}}, 
+  {{ 16, 233, 249, 249}}, {{ 21, 239, 250, 250}}, {{ 22, 237, 251, 251}}, 
+  {{ 31, 227, 252, 252}}, {{ 28, 225, 253, 253}}, {{ 25, 231, 254, 254}}, 
+  {{ 26, 229, 255, 255}}, 
+ },
+ {
+  {{  0,   0,   0,   0}}, {{  1,   3,   2,   1}}, {{  2,   6,   4,   2}}, 
+  {{  3,   5,   6,   3}}, {{  4,  12,   8,   4}}, {{  5,  15,  10,   5}}, 
+  {{  6,  10,  12,   6}}, {{  7,   9,  14,   7}}, {{  8,  24,  16,   8}}, 
+  {{  9,  27,  18,   9}}, {{ 10,  30,  20,  10}}, {{ 11,  29,  22,  11}}, 
+  {{ 12,  20,  24,  12}}, {{ 13,  23,  26,  13}}, {{ 14,  18,  28,  14}}, 
+  {{ 15,  17,  30,  15}}, {{ 16,  48,  32,  16}}, {{ 17,  51,  34,  17}}, 
+  {{ 18,  54,  36,  18}}, {{ 19,  53,  38,  19}}, {{ 20,  60,  40,  20}}, 
+  {{ 21,  63,  42,  21}}, {{ 22,  58,  44,  22}}, {{ 23,  57,  46,  23}}, 
+  {{ 24,  40,  48,  24}}, {{ 25,  43,  50,  25}}, {{ 26,  46,  52,  26}}, 
+  {{ 27,  45,  54,  27}}, {{ 28,  36,  56,  28}}, {{ 29,  39,  58,  29}}, 
+  {{ 30,  34,  60,  30}}, {{ 31,  33,  62,  31}}, {{ 32,  96,  64,  32}}, 
+  {{ 33,  99,  66,  33}}, {{ 34, 102,  68,  34}}, {{ 35, 101,  70,  35}}, 
+  {{ 36, 108,  72,  36}}, {{ 37, 111,  74,  37}}, {{ 38, 106,  76,  38}}, 
+  {{ 39, 105,  78,  39}}, {{ 40, 120,  80,  40}}, {{ 41, 123,  82,  41}}, 
+  {{ 42, 126,  84,  42}}, {{ 43, 125,  86,  43}}, {{ 44, 116,  88,  44}}, 
+  {{ 45, 119,  90,  45}}, {{ 46, 114,  92,  46}}, {{ 47, 113,  94,  47}}, 
+  {{ 48,  80,  96,  48}}, {{ 49,  83,  98,  49}}, {{ 50,  86, 100,  50}}, 
+  {{ 51,  85, 102,  51}}, {{ 52,  92, 104,  52}}, {{ 53,  95, 106,  53}}, 
+  {{ 54,  90, 108,  54}}, {{ 55,  89, 110,  55}}, {{ 56,  72, 112,  56}}, 
+  {{ 57,  75, 114,  57}}, {{ 58,  78, 116,  58}}, {{ 59,  77, 118,  59}}, 
+  {{ 60,  68, 120,  60}}, {{ 61,  71, 122,  61}}, {{ 62,  66, 124,  62}}, 
+  {{ 63,  65, 126,  63}}, {{ 64, 192, 128,  64}}, {{ 65, 195, 130,  65}}, 
+  {{ 66, 198, 132,  66}}, {{ 67, 197, 134,  67}}, {{ 68, 204, 136,  68}}, 
+  {{ 69, 207, 138,  69}}, {{ 70, 202, 140,  70}}, {{ 71, 201, 142,  71}}, 
+  {{ 72, 216, 144,  72}}, {{ 73, 219, 146,  73}}, {{ 74, 222, 148,  74}}, 
+  {{ 75, 221, 150,  75}}, {{ 76, 212, 152,  76}}, {{ 77, 215, 154,  77}}, 
+  {{ 78, 210, 156,  78}}, {{ 79, 209, 158,  79}}, {{ 80, 240, 160,  80}}, 
+  {{ 81, 243, 162,  81}}, {{ 82, 246, 164,  82}}, {{ 83, 245, 166,  83}}, 
+  {{ 84, 252, 168,  84}}, {{ 85, 255, 170,  85}}, {{ 86, 250, 172,  86}}, 
+  {{ 87, 249, 174,  87}}, {{ 88, 232, 176,  88}}, {{ 89, 235, 178,  89}}, 
+  {{ 90, 238, 180,  90}}, {{ 91, 237, 182,  91}}, {{ 92, 228, 184,  92}}, 
+  {{ 93, 231, 186,  93}}, {{ 94, 226, 188,  94}}, {{ 95, 225, 190,  95}}, 
+  {{ 96, 160, 192,  96}}, {{ 97, 163, 194,  97}}, {{ 98, 166, 196,  98}}, 
+  {{ 99, 165, 198,  99}}, {{100, 172, 200, 100}}, {{101, 175, 202, 101}}, 
+  {{102, 170, 204, 102}}, {{103, 169, 206, 103}}, {{104, 184, 208, 104}}, 
+  {{105, 187, 210, 105}}, {{106, 190, 212, 106}}, {{107, 189, 214, 107}}, 
+  {{108, 180, 216, 108}}, {{109, 183, 218, 109}}, {{110, 178, 220, 110}}, 
+  {{111, 177, 222, 111}}, {{112, 144, 224, 112}}, {{113, 147, 226, 113}}, 
+  {{114, 150, 228, 114}}, {{115, 149, 230, 115}}, {{116, 156, 232, 116}}, 
+  {{117, 159, 234, 117}}, {{118, 154, 236, 118}}, {{119, 153, 238, 119}}, 
+  {{120, 136, 240, 120}}, {{121, 139, 242, 121}}, {{122, 142, 244, 122}}, 
+  {{123, 141, 246, 123}}, {{124, 132, 248, 124}}, {{125, 135, 250, 125}}, 
+  {{126, 130, 252, 126}}, {{127, 129, 254, 127}}, {{128, 155,  27, 128}}, 
+  {{129, 152,  25, 129}}, {{130, 157,  31, 130}}, {{131, 158,  29, 131}}, 
+  {{132, 151,  19, 132}}, {{133, 148,  17, 133}}, {{134, 145,  23, 134}}, 
+  {{135, 146,  21, 135}}, {{136, 131,  11, 136}}, {{137, 128,   9, 137}}, 
+  {{138, 133,  15, 138}}, {{139, 134,  13, 139}}, {{140, 143,   3, 140}}, 
+  {{141, 140,   1, 141}}, {{142, 137,   7, 142}}, {{143, 138,   5, 143}}, 
+  {{144, 171,  59, 144}}, {{145, 168,  57, 145}}, {{146, 173,  63, 146}}, 
+  {{147, 174,  61, 147}}, {{148, 167,  51, 148}}, {{149, 164,  49, 149}}, 
+  {{150, 161,  55, 150}}, {{151, 162,  53, 151}}, {{152, 179,  43, 152}}, 
+  {{153, 176,  41, 153}}, {{154, 181,  47, 154}}, {{155, 182,  45, 155}}, 
+  {{156, 191,  35, 156}}, {{157, 188,  33, 157}}, {{158, 185,  39, 158}}, 
+  {{159, 186,  37, 159}}, {{160, 251,  91, 160}}, {{161, 248,  89, 161}}, 
+  {{162, 253,  95, 162}}, {{163, 254,  93, 163}}, {{164, 247,  83, 164}}, 
+  {{165, 244,  81, 165}}, {{166, 241,  87, 166}}, {{167, 242,  85, 167}}, 
+  {{168, 227,  75, 168}}, {{169, 224,  73, 169}}, {{170, 229,  79, 170}}, 
+  {{171, 230,  77, 171}}, {{172, 239,  67, 172}}, {{173, 236,  65, 173}}, 
+  {{174, 233,  71, 174}}, {{175, 234,  69, 175}}, {{176, 203, 123, 176}}, 
+  {{177, 200, 121, 177}}, {{178, 205, 127, 178}}, {{179, 206, 125, 179}}, 
+  {{180, 199, 115, 180}}, {{181, 196, 113, 181}}, {{182, 193, 119, 182}}, 
+  {{183, 194, 117, 183}}, {{184, 211, 107, 184}}, {{185, 208, 105, 185}}, 
+  {{186, 213, 111, 186}}, {{187, 214, 109, 187}}, {{188, 223,  99, 188}}, 
+  {{189, 220,  97, 189}}, {{190, 217, 103, 190}}, {{191, 218, 101, 191}}, 
+  {{192,  91, 155, 192}}, {{193,  88, 153, 193}}, {{194,  93, 159, 194}}, 
+  {{195,  94, 157, 195}}, {{196,  87, 147, 196}}, {{197,  84, 145, 197}}, 
+  {{198,  81, 151, 198}}, {{199,  82, 149, 199}}, {{200,  67, 139, 200}}, 
+  {{201,  64, 137, 201}}, {{202,  69, 143, 202}}, {{203,  70, 141, 203}}, 
+  {{204,  79, 131, 204}}, {{205,  76, 129, 205}}, {{206,  73, 135, 206}}, 
+  {{207,  74, 133, 207}}, {{208, 107, 187, 208}}, {{209, 104, 185, 209}}, 
+  {{210, 109, 191, 210}}, {{211, 110, 189, 211}}, {{212, 103, 179, 212}}, 
+  {{213, 100, 177, 213}}, {{214,  97, 183, 214}}, {{215,  98, 181, 215}}, 
+  {{216, 115, 171, 216}}, {{217, 112, 169, 217}}, {{218, 117, 175, 218}}, 
+  {{219, 118, 173, 219}}, {{220, 127, 163, 220}}, {{221, 124, 161, 221}}, 
+  {{222, 121, 167, 222}}, {{223, 122, 165, 223}}, {{224,  59, 219, 224}}, 
+  {{225,  56, 217, 225}}, {{226,  61, 223, 226}}, {{227,  62, 221, 227}}, 
+  {{228,  55, 211, 228}}, {{229,  52, 209, 229}}, {{230,  49, 215, 230}}, 
+  {{231,  50, 213, 231}}, {{232,  35, 203, 232}}, {{233,  32, 201, 233}}, 
+  {{234,  37, 207, 234}}, {{235,  38, 205, 235}}, {{236,  47, 195, 236}}, 
+  {{237,  44, 193, 237}}, {{238,  41, 199, 238}}, {{239,  42, 197, 239}}, 
+  {{240,  11, 251, 240}}, {{241,   8, 249, 241}}, {{242,  13, 255, 242}}, 
+  {{243,  14, 253, 243}}, {{244,   7, 243, 244}}, {{245,   4, 241, 245}}, 
+  {{246,   1, 247, 246}}, {{247,   2, 245, 247}}, {{248,  19, 235, 248}}, 
+  {{249,  16, 233, 249}}, {{250,  21, 239, 250}}, {{251,  22, 237, 251}}, 
+  {{252,  31, 227, 252}}, {{253,  28, 225, 253}}, {{254,  25, 231, 254}}, 
+  {{255,  26, 229, 255}}, 
+ },
+ {
+  {{  0,   0,   0,   0}}, {{  1,   1,   3,   2}}, {{  2,   2,   6,   4}}, 
+  {{  3,   3,   5,   6}}, {{  4,   4,  12,   8}}, {{  5,   5,  15,  10}}, 
+  {{  6,   6,  10,  12}}, {{  7,   7,   9,  14}}, {{  8,   8,  24,  16}}, 
+  {{  9,   9,  27,  18}}, {{ 10,  10,  30,  20}}, {{ 11,  11,  29,  22}}, 
+  {{ 12,  12,  20,  24}}, {{ 13,  13,  23,  26}}, {{ 14,  14,  18,  28}}, 
+  {{ 15,  15,  17,  30}}, {{ 16,  16,  48,  32}}, {{ 17,  17,  51,  34}}, 
+  {{ 18,  18,  54,  36}}, {{ 19,  19,  53,  38}}, {{ 20,  20,  60,  40}}, 
+  {{ 21,  21,  63,  42}}, {{ 22,  22,  58,  44}}, {{ 23,  23,  57,  46}}, 
+  {{ 24,  24,  40,  48}}, {{ 25,  25,  43,  50}}, {{ 26,  26,  46,  52}}, 
+  {{ 27,  27,  45,  54}}, {{ 28,  28,  36,  56}}, {{ 29,  29,  39,  58}}, 
+  {{ 30,  30,  34,  60}}, {{ 31,  31,  33,  62}}, {{ 32,  32,  96,  64}}, 
+  {{ 33,  33,  99,  66}}, {{ 34,  34, 102,  68}}, {{ 35,  35, 101,  70}}, 
+  {{ 36,  36, 108,  72}}, {{ 37,  37, 111,  74}}, {{ 38,  38, 106,  76}}, 
+  {{ 39,  39, 105,  78}}, {{ 40,  40, 120,  80}}, {{ 41,  41, 123,  82}}, 
+  {{ 42,  42, 126,  84}}, {{ 43,  43, 125,  86}}, {{ 44,  44, 116,  88}}, 
+  {{ 45,  45, 119,  90}}, {{ 46,  46, 114,  92}}, {{ 47,  47, 113,  94}}, 
+  {{ 48,  48,  80,  96}}, {{ 49,  49,  83,  98}}, {{ 50,  50,  86, 100}}, 
+  {{ 51,  51,  85, 102}}, {{ 52,  52,  92, 104}}, {{ 53,  53,  95, 106}}, 
+  {{ 54,  54,  90, 108}}, {{ 55,  55,  89, 110}}, {{ 56,  56,  72, 112}}, 
+  {{ 57,  57,  75, 114}}, {{ 58,  58,  78, 116}}, {{ 59,  59,  77, 118}}, 
+  {{ 60,  60,  68, 120}}, {{ 61,  61,  71, 122}}, {{ 62,  62,  66, 124}}, 
+  {{ 63,  63,  65, 126}}, {{ 64,  64, 192, 128}}, {{ 65,  65, 195, 130}}, 
+  {{ 66,  66, 198, 132}}, {{ 67,  67, 197, 134}}, {{ 68,  68, 204, 136}}, 
+  {{ 69,  69, 207, 138}}, {{ 70,  70, 202, 140}}, {{ 71,  71, 201, 142}}, 
+  {{ 72,  72, 216, 144}}, {{ 73,  73, 219, 146}}, {{ 74,  74, 222, 148}}, 
+  {{ 75,  75, 221, 150}}, {{ 76,  76, 212, 152}}, {{ 77,  77, 215, 154}}, 
+  {{ 78,  78, 210, 156}}, {{ 79,  79, 209, 158}}, {{ 80,  80, 240, 160}}, 
+  {{ 81,  81, 243, 162}}, {{ 82,  82, 246, 164}}, {{ 83,  83, 245, 166}}, 
+  {{ 84,  84, 252, 168}}, {{ 85,  85, 255, 170}}, {{ 86,  86, 250, 172}}, 
+  {{ 87,  87, 249, 174}}, {{ 88,  88, 232, 176}}, {{ 89,  89, 235, 178}}, 
+  {{ 90,  90, 238, 180}}, {{ 91,  91, 237, 182}}, {{ 92,  92, 228, 184}}, 
+  {{ 93,  93, 231, 186}}, {{ 94,  94, 226, 188}}, {{ 95,  95, 225, 190}}, 
+  {{ 96,  96, 160, 192}}, {{ 97,  97, 163, 194}}, {{ 98,  98, 166, 196}}, 
+  {{ 99,  99, 165, 198}}, {{100, 100, 172, 200}}, {{101, 101, 175, 202}}, 
+  {{102, 102, 170, 204}}, {{103, 103, 169, 206}}, {{104, 104, 184, 208}}, 
+  {{105, 105, 187, 210}}, {{106, 106, 190, 212}}, {{107, 107, 189, 214}}, 
+  {{108, 108, 180, 216}}, {{109, 109, 183, 218}}, {{110, 110, 178, 220}}, 
+  {{111, 111, 177, 222}}, {{112, 112, 144, 224}}, {{113, 113, 147, 226}}, 
+  {{114, 114, 150, 228}}, {{115, 115, 149, 230}}, {{116, 116, 156, 232}}, 
+  {{117, 117, 159, 234}}, {{118, 118, 154, 236}}, {{119, 119, 153, 238}}, 
+  {{120, 120, 136, 240}}, {{121, 121, 139, 242}}, {{122, 122, 142, 244}}, 
+  {{123, 123, 141, 246}}, {{124, 124, 132, 248}}, {{125, 125, 135, 250}}, 
+  {{126, 126, 130, 252}}, {{127, 127, 129, 254}}, {{128, 128, 155,  27}}, 
+  {{129, 129, 152,  25}}, {{130, 130, 157,  31}}, {{131, 131, 158,  29}}, 
+  {{132, 132, 151,  19}}, {{133, 133, 148,  17}}, {{134, 134, 145,  23}}, 
+  {{135, 135, 146,  21}}, {{136, 136, 131,  11}}, {{137, 137, 128,   9}}, 
+  {{138, 138, 133,  15}}, {{139, 139, 134,  13}}, {{140, 140, 143,   3}}, 
+  {{141, 141, 140,   1}}, {{142, 142, 137,   7}}, {{143, 143, 138,   5}}, 
+  {{144, 144, 171,  59}}, {{145, 145, 168,  57}}, {{146, 146, 173,  63}}, 
+  {{147, 147, 174,  61}}, {{148, 148, 167,  51}}, {{149, 149, 164,  49}}, 
+  {{150, 150, 161,  55}}, {{151, 151, 162,  53}}, {{152, 152, 179,  43}}, 
+  {{153, 153, 176,  41}}, {{154, 154, 181,  47}}, {{155, 155, 182,  45}}, 
+  {{156, 156, 191,  35}}, {{157, 157, 188,  33}}, {{158, 158, 185,  39}}, 
+  {{159, 159, 186,  37}}, {{160, 160, 251,  91}}, {{161, 161, 248,  89}}, 
+  {{162, 162, 253,  95}}, {{163, 163, 254,  93}}, {{164, 164, 247,  83}}, 
+  {{165, 165, 244,  81}}, {{166, 166, 241,  87}}, {{167, 167, 242,  85}}, 
+  {{168, 168, 227,  75}}, {{169, 169, 224,  73}}, {{170, 170, 229,  79}}, 
+  {{171, 171, 230,  77}}, {{172, 172, 239,  67}}, {{173, 173, 236,  65}}, 
+  {{174, 174, 233,  71}}, {{175, 175, 234,  69}}, {{176, 176, 203, 123}}, 
+  {{177, 177, 200, 121}}, {{178, 178, 205, 127}}, {{179, 179, 206, 125}}, 
+  {{180, 180, 199, 115}}, {{181, 181, 196, 113}}, {{182, 182, 193, 119}}, 
+  {{183, 183, 194, 117}}, {{184, 184, 211, 107}}, {{185, 185, 208, 105}}, 
+  {{186, 186, 213, 111}}, {{187, 187, 214, 109}}, {{188, 188, 223,  99}}, 
+  {{189, 189, 220,  97}}, {{190, 190, 217, 103}}, {{191, 191, 218, 101}}, 
+  {{192, 192,  91, 155}}, {{193, 193,  88, 153}}, {{194, 194,  93, 159}}, 
+  {{195, 195,  94, 157}}, {{196, 196,  87, 147}}, {{197, 197,  84, 145}}, 
+  {{198, 198,  81, 151}}, {{199, 199,  82, 149}}, {{200, 200,  67, 139}}, 
+  {{201, 201,  64, 137}}, {{202, 202,  69, 143}}, {{203, 203,  70, 141}}, 
+  {{204, 204,  79, 131}}, {{205, 205,  76, 129}}, {{206, 206,  73, 135}}, 
+  {{207, 207,  74, 133}}, {{208, 208, 107, 187}}, {{209, 209, 104, 185}}, 
+  {{210, 210, 109, 191}}, {{211, 211, 110, 189}}, {{212, 212, 103, 179}}, 
+  {{213, 213, 100, 177}}, {{214, 214,  97, 183}}, {{215, 215,  98, 181}}, 
+  {{216, 216, 115, 171}}, {{217, 217, 112, 169}}, {{218, 218, 117, 175}}, 
+  {{219, 219, 118, 173}}, {{220, 220, 127, 163}}, {{221, 221, 124, 161}}, 
+  {{222, 222, 121, 167}}, {{223, 223, 122, 165}}, {{224, 224,  59, 219}}, 
+  {{225, 225,  56, 217}}, {{226, 226,  61, 223}}, {{227, 227,  62, 221}}, 
+  {{228, 228,  55, 211}}, {{229, 229,  52, 209}}, {{230, 230,  49, 215}}, 
+  {{231, 231,  50, 213}}, {{232, 232,  35, 203}}, {{233, 233,  32, 201}}, 
+  {{234, 234,  37, 207}}, {{235, 235,  38, 205}}, {{236, 236,  47, 195}}, 
+  {{237, 237,  44, 193}}, {{238, 238,  41, 199}}, {{239, 239,  42, 197}}, 
+  {{240, 240,  11, 251}}, {{241, 241,   8, 249}}, {{242, 242,  13, 255}}, 
+  {{243, 243,  14, 253}}, {{244, 244,   7, 243}}, {{245, 245,   4, 241}}, 
+  {{246, 246,   1, 247}}, {{247, 247,   2, 245}}, {{248, 248,  19, 235}}, 
+  {{249, 249,  16, 233}}, {{250, 250,  21, 239}}, {{251, 251,  22, 237}}, 
+  {{252, 252,  31, 227}}, {{253, 253,  28, 225}}, {{254, 254,  25, 231}}, 
+  {{255, 255,  26, 229}}, 
+ },
+};
+
+const word8x4 M1[4][256] = {
+ {
+  {{  0,   0,   0,   0}}, {{ 14,   9,  13,  11}}, {{ 28,  18,  26,  22}}, 
+  {{ 18,  27,  23,  29}}, {{ 56,  36,  52,  44}}, {{ 54,  45,  57,  39}}, 
+  {{ 36,  54,  46,  58}}, {{ 42,  63,  35,  49}}, {{112,  72, 104,  88}}, 
+  {{126,  65, 101,  83}}, {{108,  90, 114,  78}}, {{ 98,  83, 127,  69}}, 
+  {{ 72, 108,  92, 116}}, {{ 70, 101,  81, 127}}, {{ 84, 126,  70,  98}}, 
+  {{ 90, 119,  75, 105}}, {{224, 144, 208, 176}}, {{238, 153, 221, 187}}, 
+  {{252, 130, 202, 166}}, {{242, 139, 199, 173}}, {{216, 180, 228, 156}}, 
+  {{214, 189, 233, 151}}, {{196, 166, 254, 138}}, {{202, 175, 243, 129}}, 
+  {{144, 216, 184, 232}}, {{158, 209, 181, 227}}, {{140, 202, 162, 254}}, 
+  {{130, 195, 175, 245}}, {{168, 252, 140, 196}}, {{166, 245, 129, 207}}, 
+  {{180, 238, 150, 210}}, {{186, 231, 155, 217}}, {{219,  59, 187, 123}}, 
+  {{213,  50, 182, 112}}, {{199,  41, 161, 109}}, {{201,  32, 172, 102}}, 
+  {{227,  31, 143,  87}}, {{237,  22, 130,  92}}, {{255,  13, 149,  65}}, 
+  {{241,   4, 152,  74}}, {{171, 115, 211,  35}}, {{165, 122, 222,  40}}, 
+  {{183,  97, 201,  53}}, {{185, 104, 196,  62}}, {{147,  87, 231,  15}}, 
+  {{157,  94, 234,   4}}, {{143,  69, 253,  25}}, {{129,  76, 240,  18}}, 
+  {{ 59, 171, 107, 203}}, {{ 53, 162, 102, 192}}, {{ 39, 185, 113, 221}}, 
+  {{ 41, 176, 124, 214}}, {{  3, 143,  95, 231}}, {{ 13, 134,  82, 236}}, 
+  {{ 31, 157,  69, 241}}, {{ 17, 148,  72, 250}}, {{ 75, 227,   3, 147}}, 
+  {{ 69, 234,  14, 152}}, {{ 87, 241,  25, 133}}, {{ 89, 248,  20, 142}}, 
+  {{115, 199,  55, 191}}, {{125, 206,  58, 180}}, {{111, 213,  45, 169}}, 
+  {{ 97, 220,  32, 162}}, {{173, 118, 109, 246}}, {{163, 127,  96, 253}}, 
+  {{177, 100, 119, 224}}, {{191, 109, 122, 235}}, {{149,  82,  89, 218}}, 
+  {{155,  91,  84, 209}}, {{137,  64,  67, 204}}, {{135,  73,  78, 199}}, 
+  {{221,  62,   5, 174}}, {{211,  55,   8, 165}}, {{193,  44,  31, 184}}, 
+  {{207,  37,  18, 179}}, {{229,  26,  49, 130}}, {{235,  19,  60, 137}}, 
+  {{249,   8,  43, 148}}, {{247,   1,  38, 159}}, {{ 77, 230, 189,  70}}, 
+  {{ 67, 239, 176,  77}}, {{ 81, 244, 167,  80}}, {{ 95, 253, 170,  91}}, 
+  {{117, 194, 137, 106}}, {{123, 203, 132,  97}}, {{105, 208, 147, 124}}, 
+  {{103, 217, 158, 119}}, {{ 61, 174, 213,  30}}, {{ 51, 167, 216,  21}}, 
+  {{ 33, 188, 207,   8}}, {{ 47, 181, 194,   3}}, {{  5, 138, 225,  50}}, 
+  {{ 11, 131, 236,  57}}, {{ 25, 152, 251,  36}}, {{ 23, 145, 246,  47}}, 
+  {{118,  77, 214, 141}}, {{120,  68, 219, 134}}, {{106,  95, 204, 155}}, 
+  {{100,  86, 193, 144}}, {{ 78, 105, 226, 161}}, {{ 64,  96, 239, 170}}, 
+  {{ 82, 123, 248, 183}}, {{ 92, 114, 245, 188}}, {{  6,   5, 190, 213}}, 
+  {{  8,  12, 179, 222}}, {{ 26,  23, 164, 195}}, {{ 20,  30, 169, 200}}, 
+  {{ 62,  33, 138, 249}}, {{ 48,  40, 135, 242}}, {{ 34,  51, 144, 239}}, 
+  {{ 44,  58, 157, 228}}, {{150, 221,   6,  61}}, {{152, 212,  11,  54}}, 
+  {{138, 207,  28,  43}}, {{132, 198,  17,  32}}, {{174, 249,  50,  17}}, 
+  {{160, 240,  63,  26}}, {{178, 235,  40,   7}}, {{188, 226,  37,  12}}, 
+  {{230, 149, 110, 101}}, {{232, 156,  99, 110}}, {{250, 135, 116, 115}}, 
+  {{244, 142, 121, 120}}, {{222, 177,  90,  73}}, {{208, 184,  87,  66}}, 
+  {{194, 163,  64,  95}}, {{204, 170,  77,  84}}, {{ 65, 236, 218, 247}}, 
+  {{ 79, 229, 215, 252}}, {{ 93, 254, 192, 225}}, {{ 83, 247, 205, 234}}, 
+  {{121, 200, 238, 219}}, {{119, 193, 227, 208}}, {{101, 218, 244, 205}}, 
+  {{107, 211, 249, 198}}, {{ 49, 164, 178, 175}}, {{ 63, 173, 191, 164}}, 
+  {{ 45, 182, 168, 185}}, {{ 35, 191, 165, 178}}, {{  9, 128, 134, 131}}, 
+  {{  7, 137, 139, 136}}, {{ 21, 146, 156, 149}}, {{ 27, 155, 145, 158}}, 
+  {{161, 124,  10,  71}}, {{175, 117,   7,  76}}, {{189, 110,  16,  81}}, 
+  {{179, 103,  29,  90}}, {{153,  88,  62, 107}}, {{151,  81,  51,  96}}, 
+  {{133,  74,  36, 125}}, {{139,  67,  41, 118}}, {{209,  52,  98,  31}}, 
+  {{223,  61, 111,  20}}, {{205,  38, 120,   9}}, {{195,  47, 117,   2}}, 
+  {{233,  16,  86,  51}}, {{231,  25,  91,  56}}, {{245,   2,  76,  37}}, 
+  {{251,  11,  65,  46}}, {{154, 215,  97, 140}}, {{148, 222, 108, 135}}, 
+  {{134, 197, 123, 154}}, {{136, 204, 118, 145}}, {{162, 243,  85, 160}}, 
+  {{172, 250,  88, 171}}, {{190, 225,  79, 182}}, {{176, 232,  66, 189}}, 
+  {{234, 159,   9, 212}}, {{228, 150,   4, 223}}, {{246, 141,  19, 194}}, 
+  {{248, 132,  30, 201}}, {{210, 187,  61, 248}}, {{220, 178,  48, 243}}, 
+  {{206, 169,  39, 238}}, {{192, 160,  42, 229}}, {{122,  71, 177,  60}}, 
+  {{116,  78, 188,  55}}, {{102,  85, 171,  42}}, {{104,  92, 166,  33}}, 
+  {{ 66,  99, 133,  16}}, {{ 76, 106, 136,  27}}, {{ 94, 113, 159,   6}}, 
+  {{ 80, 120, 146,  13}}, {{ 10,  15, 217, 100}}, {{  4,   6, 212, 111}}, 
+  {{ 22,  29, 195, 114}}, {{ 24,  20, 206, 121}}, {{ 50,  43, 237,  72}}, 
+  {{ 60,  34, 224,  67}}, {{ 46,  57, 247,  94}}, {{ 32,  48, 250,  85}}, 
+  {{236, 154, 183,   1}}, {{226, 147, 186,  10}}, {{240, 136, 173,  23}}, 
+  {{254, 129, 160,  28}}, {{212, 190, 131,  45}}, {{218, 183, 142,  38}}, 
+  {{200, 172, 153,  59}}, {{198, 165, 148,  48}}, {{156, 210, 223,  89}}, 
+  {{146, 219, 210,  82}}, {{128, 192, 197,  79}}, {{142, 201, 200,  68}}, 
+  {{164, 246, 235, 117}}, {{170, 255, 230, 126}}, {{184, 228, 241,  99}}, 
+  {{182, 237, 252, 104}}, {{ 12,  10, 103, 177}}, {{  2,   3, 106, 186}}, 
+  {{ 16,  24, 125, 167}}, {{ 30,  17, 112, 172}}, {{ 52,  46,  83, 157}}, 
+  {{ 58,  39,  94, 150}}, {{ 40,  60,  73, 139}}, {{ 38,  53,  68, 128}}, 
+  {{124,  66,  15, 233}}, {{114,  75,   2, 226}}, {{ 96,  80,  21, 255}}, 
+  {{110,  89,  24, 244}}, {{ 68, 102,  59, 197}}, {{ 74, 111,  54, 206}}, 
+  {{ 88, 116,  33, 211}}, {{ 86, 125,  44, 216}}, {{ 55, 161,  12, 122}}, 
+  {{ 57, 168,   1, 113}}, {{ 43, 179,  22, 108}}, {{ 37, 186,  27, 103}}, 
+  {{ 15, 133,  56,  86}}, {{  1, 140,  53,  93}}, {{ 19, 151,  34,  64}}, 
+  {{ 29, 158,  47,  75}}, {{ 71, 233, 100,  34}}, {{ 73, 224, 105,  41}}, 
+  {{ 91, 251, 126,  52}}, {{ 85, 242, 115,  63}}, {{127, 205,  80,  14}}, 
+  {{113, 196,  93,   5}}, {{ 99, 223,  74,  24}}, {{109, 214,  71,  19}}, 
+  {{215,  49, 220, 202}}, {{217,  56, 209, 193}}, {{203,  35, 198, 220}}, 
+  {{197,  42, 203, 215}}, {{239,  21, 232, 230}}, {{225,  28, 229, 237}}, 
+  {{243,   7, 242, 240}}, {{253,  14, 255, 251}}, {{167, 121, 180, 146}}, 
+  {{169, 112, 185, 153}}, {{187, 107, 174, 132}}, {{181,  98, 163, 143}}, 
+  {{159,  93, 128, 190}}, {{145,  84, 141, 181}}, {{131,  79, 154, 168}}, 
+  {{141,  70, 151, 163}}, 
+ },
+ {
+  {{  0,   0,   0,   0}}, {{ 11,  14,   9,  13}}, {{ 22,  28,  18,  26}}, 
+  {{ 29,  18,  27,  23}}, {{ 44,  56,  36,  52}}, {{ 39,  54,  45,  57}}, 
+  {{ 58,  36,  54,  46}}, {{ 49,  42,  63,  35}}, {{ 88, 112,  72, 104}}, 
+  {{ 83, 126,  65, 101}}, {{ 78, 108,  90, 114}}, {{ 69,  98,  83, 127}}, 
+  {{116,  72, 108,  92}}, {{127,  70, 101,  81}}, {{ 98,  84, 126,  70}}, 
+  {{105,  90, 119,  75}}, {{176, 224, 144, 208}}, {{187, 238, 153, 221}}, 
+  {{166, 252, 130, 202}}, {{173, 242, 139, 199}}, {{156, 216, 180, 228}}, 
+  {{151, 214, 189, 233}}, {{138, 196, 166, 254}}, {{129, 202, 175, 243}}, 
+  {{232, 144, 216, 184}}, {{227, 158, 209, 181}}, {{254, 140, 202, 162}}, 
+  {{245, 130, 195, 175}}, {{196, 168, 252, 140}}, {{207, 166, 245, 129}}, 
+  {{210, 180, 238, 150}}, {{217, 186, 231, 155}}, {{123, 219,  59, 187}}, 
+  {{112, 213,  50, 182}}, {{109, 199,  41, 161}}, {{102, 201,  32, 172}}, 
+  {{ 87, 227,  31, 143}}, {{ 92, 237,  22, 130}}, {{ 65, 255,  13, 149}}, 
+  {{ 74, 241,   4, 152}}, {{ 35, 171, 115, 211}}, {{ 40, 165, 122, 222}}, 
+  {{ 53, 183,  97, 201}}, {{ 62, 185, 104, 196}}, {{ 15, 147,  87, 231}}, 
+  {{  4, 157,  94, 234}}, {{ 25, 143,  69, 253}}, {{ 18, 129,  76, 240}}, 
+  {{203,  59, 171, 107}}, {{192,  53, 162, 102}}, {{221,  39, 185, 113}}, 
+  {{214,  41, 176, 124}}, {{231,   3, 143,  95}}, {{236,  13, 134,  82}}, 
+  {{241,  31, 157,  69}}, {{250,  17, 148,  72}}, {{147,  75, 227,   3}}, 
+  {{152,  69, 234,  14}}, {{133,  87, 241,  25}}, {{142,  89, 248,  20}}, 
+  {{191, 115, 199,  55}}, {{180, 125, 206,  58}}, {{169, 111, 213,  45}}, 
+  {{162,  97, 220,  32}}, {{246, 173, 118, 109}}, {{253, 163, 127,  96}}, 
+  {{224, 177, 100, 119}}, {{235, 191, 109, 122}}, {{218, 149,  82,  89}}, 
+  {{209, 155,  91,  84}}, {{204, 137,  64,  67}}, {{199, 135,  73,  78}}, 
+  {{174, 221,  62,   5}}, {{165, 211,  55,   8}}, {{184, 193,  44,  31}}, 
+  {{179, 207,  37,  18}}, {{130, 229,  26,  49}}, {{137, 235,  19,  60}}, 
+  {{148, 249,   8,  43}}, {{159, 247,   1,  38}}, {{ 70,  77, 230, 189}}, 
+  {{ 77,  67, 239, 176}}, {{ 80,  81, 244, 167}}, {{ 91,  95, 253, 170}}, 
+  {{106, 117, 194, 137}}, {{ 97, 123, 203, 132}}, {{124, 105, 208, 147}}, 
+  {{119, 103, 217, 158}}, {{ 30,  61, 174, 213}}, {{ 21,  51, 167, 216}}, 
+  {{  8,  33, 188, 207}}, {{  3,  47, 181, 194}}, {{ 50,   5, 138, 225}}, 
+  {{ 57,  11, 131, 236}}, {{ 36,  25, 152, 251}}, {{ 47,  23, 145, 246}}, 
+  {{141, 118,  77, 214}}, {{134, 120,  68, 219}}, {{155, 106,  95, 204}}, 
+  {{144, 100,  86, 193}}, {{161,  78, 105, 226}}, {{170,  64,  96, 239}}, 
+  {{183,  82, 123, 248}}, {{188,  92, 114, 245}}, {{213,   6,   5, 190}}, 
+  {{222,   8,  12, 179}}, {{195,  26,  23, 164}}, {{200,  20,  30, 169}}, 
+  {{249,  62,  33, 138}}, {{242,  48,  40, 135}}, {{239,  34,  51, 144}}, 
+  {{228,  44,  58, 157}}, {{ 61, 150, 221,   6}}, {{ 54, 152, 212,  11}}, 
+  {{ 43, 138, 207,  28}}, {{ 32, 132, 198,  17}}, {{ 17, 174, 249,  50}}, 
+  {{ 26, 160, 240,  63}}, {{  7, 178, 235,  40}}, {{ 12, 188, 226,  37}}, 
+  {{101, 230, 149, 110}}, {{110, 232, 156,  99}}, {{115, 250, 135, 116}}, 
+  {{120, 244, 142, 121}}, {{ 73, 222, 177,  90}}, {{ 66, 208, 184,  87}}, 
+  {{ 95, 194, 163,  64}}, {{ 84, 204, 170,  77}}, {{247,  65, 236, 218}}, 
+  {{252,  79, 229, 215}}, {{225,  93, 254, 192}}, {{234,  83, 247, 205}}, 
+  {{219, 121, 200, 238}}, {{208, 119, 193, 227}}, {{205, 101, 218, 244}}, 
+  {{198, 107, 211, 249}}, {{175,  49, 164, 178}}, {{164,  63, 173, 191}}, 
+  {{185,  45, 182, 168}}, {{178,  35, 191, 165}}, {{131,   9, 128, 134}}, 
+  {{136,   7, 137, 139}}, {{149,  21, 146, 156}}, {{158,  27, 155, 145}}, 
+  {{ 71, 161, 124,  10}}, {{ 76, 175, 117,   7}}, {{ 81, 189, 110,  16}}, 
+  {{ 90, 179, 103,  29}}, {{107, 153,  88,  62}}, {{ 96, 151,  81,  51}}, 
+  {{125, 133,  74,  36}}, {{118, 139,  67,  41}}, {{ 31, 209,  52,  98}}, 
+  {{ 20, 223,  61, 111}}, {{  9, 205,  38, 120}}, {{  2, 195,  47, 117}}, 
+  {{ 51, 233,  16,  86}}, {{ 56, 231,  25,  91}}, {{ 37, 245,   2,  76}}, 
+  {{ 46, 251,  11,  65}}, {{140, 154, 215,  97}}, {{135, 148, 222, 108}}, 
+  {{154, 134, 197, 123}}, {{145, 136, 204, 118}}, {{160, 162, 243,  85}}, 
+  {{171, 172, 250,  88}}, {{182, 190, 225,  79}}, {{189, 176, 232,  66}}, 
+  {{212, 234, 159,   9}}, {{223, 228, 150,   4}}, {{194, 246, 141,  19}}, 
+  {{201, 248, 132,  30}}, {{248, 210, 187,  61}}, {{243, 220, 178,  48}}, 
+  {{238, 206, 169,  39}}, {{229, 192, 160,  42}}, {{ 60, 122,  71, 177}}, 
+  {{ 55, 116,  78, 188}}, {{ 42, 102,  85, 171}}, {{ 33, 104,  92, 166}}, 
+  {{ 16,  66,  99, 133}}, {{ 27,  76, 106, 136}}, {{  6,  94, 113, 159}}, 
+  {{ 13,  80, 120, 146}}, {{100,  10,  15, 217}}, {{111,   4,   6, 212}}, 
+  {{114,  22,  29, 195}}, {{121,  24,  20, 206}}, {{ 72,  50,  43, 237}}, 
+  {{ 67,  60,  34, 224}}, {{ 94,  46,  57, 247}}, {{ 85,  32,  48, 250}}, 
+  {{  1, 236, 154, 183}}, {{ 10, 226, 147, 186}}, {{ 23, 240, 136, 173}}, 
+  {{ 28, 254, 129, 160}}, {{ 45, 212, 190, 131}}, {{ 38, 218, 183, 142}}, 
+  {{ 59, 200, 172, 153}}, {{ 48, 198, 165, 148}}, {{ 89, 156, 210, 223}}, 
+  {{ 82, 146, 219, 210}}, {{ 79, 128, 192, 197}}, {{ 68, 142, 201, 200}}, 
+  {{117, 164, 246, 235}}, {{126, 170, 255, 230}}, {{ 99, 184, 228, 241}}, 
+  {{104, 182, 237, 252}}, {{177,  12,  10, 103}}, {{186,   2,   3, 106}}, 
+  {{167,  16,  24, 125}}, {{172,  30,  17, 112}}, {{157,  52,  46,  83}}, 
+  {{150,  58,  39,  94}}, {{139,  40,  60,  73}}, {{128,  38,  53,  68}}, 
+  {{233, 124,  66,  15}}, {{226, 114,  75,   2}}, {{255,  96,  80,  21}}, 
+  {{244, 110,  89,  24}}, {{197,  68, 102,  59}}, {{206,  74, 111,  54}}, 
+  {{211,  88, 116,  33}}, {{216,  86, 125,  44}}, {{122,  55, 161,  12}}, 
+  {{113,  57, 168,   1}}, {{108,  43, 179,  22}}, {{103,  37, 186,  27}}, 
+  {{ 86,  15, 133,  56}}, {{ 93,   1, 140,  53}}, {{ 64,  19, 151,  34}}, 
+  {{ 75,  29, 158,  47}}, {{ 34,  71, 233, 100}}, {{ 41,  73, 224, 105}}, 
+  {{ 52,  91, 251, 126}}, {{ 63,  85, 242, 115}}, {{ 14, 127, 205,  80}}, 
+  {{  5, 113, 196,  93}}, {{ 24,  99, 223,  74}}, {{ 19, 109, 214,  71}}, 
+  {{202, 215,  49, 220}}, {{193, 217,  56, 209}}, {{220, 203,  35, 198}}, 
+  {{215, 197,  42, 203}}, {{230, 239,  21, 232}}, {{237, 225,  28, 229}}, 
+  {{240, 243,   7, 242}}, {{251, 253,  14, 255}}, {{146, 167, 121, 180}}, 
+  {{153, 169, 112, 185}}, {{132, 187, 107, 174}}, {{143, 181,  98, 163}}, 
+  {{190, 159,  93, 128}}, {{181, 145,  84, 141}}, {{168, 131,  79, 154}}, 
+  {{163, 141,  70, 151}}, 
+ },
+ {
+  {{  0,   0,   0,   0}}, {{ 13,  11,  14,   9}}, {{ 26,  22,  28,  18}}, 
+  {{ 23,  29,  18,  27}}, {{ 52,  44,  56,  36}}, {{ 57,  39,  54,  45}}, 
+  {{ 46,  58,  36,  54}}, {{ 35,  49,  42,  63}}, {{104,  88, 112,  72}}, 
+  {{101,  83, 126,  65}}, {{114,  78, 108,  90}}, {{127,  69,  98,  83}}, 
+  {{ 92, 116,  72, 108}}, {{ 81, 127,  70, 101}}, {{ 70,  98,  84, 126}}, 
+  {{ 75, 105,  90, 119}}, {{208, 176, 224, 144}}, {{221, 187, 238, 153}}, 
+  {{202, 166, 252, 130}}, {{199, 173, 242, 139}}, {{228, 156, 216, 180}}, 
+  {{233, 151, 214, 189}}, {{254, 138, 196, 166}}, {{243, 129, 202, 175}}, 
+  {{184, 232, 144, 216}}, {{181, 227, 158, 209}}, {{162, 254, 140, 202}}, 
+  {{175, 245, 130, 195}}, {{140, 196, 168, 252}}, {{129, 207, 166, 245}}, 
+  {{150, 210, 180, 238}}, {{155, 217, 186, 231}}, {{187, 123, 219,  59}}, 
+  {{182, 112, 213,  50}}, {{161, 109, 199,  41}}, {{172, 102, 201,  32}}, 
+  {{143,  87, 227,  31}}, {{130,  92, 237,  22}}, {{149,  65, 255,  13}}, 
+  {{152,  74, 241,   4}}, {{211,  35, 171, 115}}, {{222,  40, 165, 122}}, 
+  {{201,  53, 183,  97}}, {{196,  62, 185, 104}}, {{231,  15, 147,  87}}, 
+  {{234,   4, 157,  94}}, {{253,  25, 143,  69}}, {{240,  18, 129,  76}}, 
+  {{107, 203,  59, 171}}, {{102, 192,  53, 162}}, {{113, 221,  39, 185}}, 
+  {{124, 214,  41, 176}}, {{ 95, 231,   3, 143}}, {{ 82, 236,  13, 134}}, 
+  {{ 69, 241,  31, 157}}, {{ 72, 250,  17, 148}}, {{  3, 147,  75, 227}}, 
+  {{ 14, 152,  69, 234}}, {{ 25, 133,  87, 241}}, {{ 20, 142,  89, 248}}, 
+  {{ 55, 191, 115, 199}}, {{ 58, 180, 125, 206}}, {{ 45, 169, 111, 213}}, 
+  {{ 32, 162,  97, 220}}, {{109, 246, 173, 118}}, {{ 96, 253, 163, 127}}, 
+  {{119, 224, 177, 100}}, {{122, 235, 191, 109}}, {{ 89, 218, 149,  82}}, 
+  {{ 84, 209, 155,  91}}, {{ 67, 204, 137,  64}}, {{ 78, 199, 135,  73}}, 
+  {{  5, 174, 221,  62}}, {{  8, 165, 211,  55}}, {{ 31, 184, 193,  44}}, 
+  {{ 18, 179, 207,  37}}, {{ 49, 130, 229,  26}}, {{ 60, 137, 235,  19}}, 
+  {{ 43, 148, 249,   8}}, {{ 38, 159, 247,   1}}, {{189,  70,  77, 230}}, 
+  {{176,  77,  67, 239}}, {{167,  80,  81, 244}}, {{170,  91,  95, 253}}, 
+  {{137, 106, 117, 194}}, {{132,  97, 123, 203}}, {{147, 124, 105, 208}}, 
+  {{158, 119, 103, 217}}, {{213,  30,  61, 174}}, {{216,  21,  51, 167}}, 
+  {{207,   8,  33, 188}}, {{194,   3,  47, 181}}, {{225,  50,   5, 138}}, 
+  {{236,  57,  11, 131}}, {{251,  36,  25, 152}}, {{246,  47,  23, 145}}, 
+  {{214, 141, 118,  77}}, {{219, 134, 120,  68}}, {{204, 155, 106,  95}}, 
+  {{193, 144, 100,  86}}, {{226, 161,  78, 105}}, {{239, 170,  64,  96}}, 
+  {{248, 183,  82, 123}}, {{245, 188,  92, 114}}, {{190, 213,   6,   5}}, 
+  {{179, 222,   8,  12}}, {{164, 195,  26,  23}}, {{169, 200,  20,  30}}, 
+  {{138, 249,  62,  33}}, {{135, 242,  48,  40}}, {{144, 239,  34,  51}}, 
+  {{157, 228,  44,  58}}, {{  6,  61, 150, 221}}, {{ 11,  54, 152, 212}}, 
+  {{ 28,  43, 138, 207}}, {{ 17,  32, 132, 198}}, {{ 50,  17, 174, 249}}, 
+  {{ 63,  26, 160, 240}}, {{ 40,   7, 178, 235}}, {{ 37,  12, 188, 226}}, 
+  {{110, 101, 230, 149}}, {{ 99, 110, 232, 156}}, {{116, 115, 250, 135}}, 
+  {{121, 120, 244, 142}}, {{ 90,  73, 222, 177}}, {{ 87,  66, 208, 184}}, 
+  {{ 64,  95, 194, 163}}, {{ 77,  84, 204, 170}}, {{218, 247,  65, 236}}, 
+  {{215, 252,  79, 229}}, {{192, 225,  93, 254}}, {{205, 234,  83, 247}}, 
+  {{238, 219, 121, 200}}, {{227, 208, 119, 193}}, {{244, 205, 101, 218}}, 
+  {{249, 198, 107, 211}}, {{178, 175,  49, 164}}, {{191, 164,  63, 173}}, 
+  {{168, 185,  45, 182}}, {{165, 178,  35, 191}}, {{134, 131,   9, 128}}, 
+  {{139, 136,   7, 137}}, {{156, 149,  21, 146}}, {{145, 158,  27, 155}}, 
+  {{ 10,  71, 161, 124}}, {{  7,  76, 175, 117}}, {{ 16,  81, 189, 110}}, 
+  {{ 29,  90, 179, 103}}, {{ 62, 107, 153,  88}}, {{ 51,  96, 151,  81}}, 
+  {{ 36, 125, 133,  74}}, {{ 41, 118, 139,  67}}, {{ 98,  31, 209,  52}}, 
+  {{111,  20, 223,  61}}, {{120,   9, 205,  38}}, {{117,   2, 195,  47}}, 
+  {{ 86,  51, 233,  16}}, {{ 91,  56, 231,  25}}, {{ 76,  37, 245,   2}}, 
+  {{ 65,  46, 251,  11}}, {{ 97, 140, 154, 215}}, {{108, 135, 148, 222}}, 
+  {{123, 154, 134, 197}}, {{118, 145, 136, 204}}, {{ 85, 160, 162, 243}}, 
+  {{ 88, 171, 172, 250}}, {{ 79, 182, 190, 225}}, {{ 66, 189, 176, 232}}, 
+  {{  9, 212, 234, 159}}, {{  4, 223, 228, 150}}, {{ 19, 194, 246, 141}}, 
+  {{ 30, 201, 248, 132}}, {{ 61, 248, 210, 187}}, {{ 48, 243, 220, 178}}, 
+  {{ 39, 238, 206, 169}}, {{ 42, 229, 192, 160}}, {{177,  60, 122,  71}}, 
+  {{188,  55, 116,  78}}, {{171,  42, 102,  85}}, {{166,  33, 104,  92}}, 
+  {{133,  16,  66,  99}}, {{136,  27,  76, 106}}, {{159,   6,  94, 113}}, 
+  {{146,  13,  80, 120}}, {{217, 100,  10,  15}}, {{212, 111,   4,   6}}, 
+  {{195, 114,  22,  29}}, {{206, 121,  24,  20}}, {{237,  72,  50,  43}}, 
+  {{224,  67,  60,  34}}, {{247,  94,  46,  57}}, {{250,  85,  32,  48}}, 
+  {{183,   1, 236, 154}}, {{186,  10, 226, 147}}, {{173,  23, 240, 136}}, 
+  {{160,  28, 254, 129}}, {{131,  45, 212, 190}}, {{142,  38, 218, 183}}, 
+  {{153,  59, 200, 172}}, {{148,  48, 198, 165}}, {{223,  89, 156, 210}}, 
+  {{210,  82, 146, 219}}, {{197,  79, 128, 192}}, {{200,  68, 142, 201}}, 
+  {{235, 117, 164, 246}}, {{230, 126, 170, 255}}, {{241,  99, 184, 228}}, 
+  {{252, 104, 182, 237}}, {{103, 177,  12,  10}}, {{106, 186,   2,   3}}, 
+  {{125, 167,  16,  24}}, {{112, 172,  30,  17}}, {{ 83, 157,  52,  46}}, 
+  {{ 94, 150,  58,  39}}, {{ 73, 139,  40,  60}}, {{ 68, 128,  38,  53}}, 
+  {{ 15, 233, 124,  66}}, {{  2, 226, 114,  75}}, {{ 21, 255,  96,  80}}, 
+  {{ 24, 244, 110,  89}}, {{ 59, 197,  68, 102}}, {{ 54, 206,  74, 111}}, 
+  {{ 33, 211,  88, 116}}, {{ 44, 216,  86, 125}}, {{ 12, 122,  55, 161}}, 
+  {{  1, 113,  57, 168}}, {{ 22, 108,  43, 179}}, {{ 27, 103,  37, 186}}, 
+  {{ 56,  86,  15, 133}}, {{ 53,  93,   1, 140}}, {{ 34,  64,  19, 151}}, 
+  {{ 47,  75,  29, 158}}, {{100,  34,  71, 233}}, {{105,  41,  73, 224}}, 
+  {{126,  52,  91, 251}}, {{115,  63,  85, 242}}, {{ 80,  14, 127, 205}}, 
+  {{ 93,   5, 113, 196}}, {{ 74,  24,  99, 223}}, {{ 71,  19, 109, 214}}, 
+  {{220, 202, 215,  49}}, {{209, 193, 217,  56}}, {{198, 220, 203,  35}}, 
+  {{203, 215, 197,  42}}, {{232, 230, 239,  21}}, {{229, 237, 225,  28}}, 
+  {{242, 240, 243,   7}}, {{255, 251, 253,  14}}, {{180, 146, 167, 121}}, 
+  {{185, 153, 169, 112}}, {{174, 132, 187, 107}}, {{163, 143, 181,  98}}, 
+  {{128, 190, 159,  93}}, {{141, 181, 145,  84}}, {{154, 168, 131,  79}}, 
+  {{151, 163, 141,  70}}, 
+ },
+ {
+  {{  0,   0,   0,   0}}, {{  9,  13,  11,  14}}, {{ 18,  26,  22,  28}}, 
+  {{ 27,  23,  29,  18}}, {{ 36,  52,  44,  56}}, {{ 45,  57,  39,  54}}, 
+  {{ 54,  46,  58,  36}}, {{ 63,  35,  49,  42}}, {{ 72, 104,  88, 112}}, 
+  {{ 65, 101,  83, 126}}, {{ 90, 114,  78, 108}}, {{ 83, 127,  69,  98}}, 
+  {{108,  92, 116,  72}}, {{101,  81, 127,  70}}, {{126,  70,  98,  84}}, 
+  {{119,  75, 105,  90}}, {{144, 208, 176, 224}}, {{153, 221, 187, 238}}, 
+  {{130, 202, 166, 252}}, {{139, 199, 173, 242}}, {{180, 228, 156, 216}}, 
+  {{189, 233, 151, 214}}, {{166, 254, 138, 196}}, {{175, 243, 129, 202}}, 
+  {{216, 184, 232, 144}}, {{209, 181, 227, 158}}, {{202, 162, 254, 140}}, 
+  {{195, 175, 245, 130}}, {{252, 140, 196, 168}}, {{245, 129, 207, 166}}, 
+  {{238, 150, 210, 180}}, {{231, 155, 217, 186}}, {{ 59, 187, 123, 219}}, 
+  {{ 50, 182, 112, 213}}, {{ 41, 161, 109, 199}}, {{ 32, 172, 102, 201}}, 
+  {{ 31, 143,  87, 227}}, {{ 22, 130,  92, 237}}, {{ 13, 149,  65, 255}}, 
+  {{  4, 152,  74, 241}}, {{115, 211,  35, 171}}, {{122, 222,  40, 165}}, 
+  {{ 97, 201,  53, 183}}, {{104, 196,  62, 185}}, {{ 87, 231,  15, 147}}, 
+  {{ 94, 234,   4, 157}}, {{ 69, 253,  25, 143}}, {{ 76, 240,  18, 129}}, 
+  {{171, 107, 203,  59}}, {{162, 102, 192,  53}}, {{185, 113, 221,  39}}, 
+  {{176, 124, 214,  41}}, {{143,  95, 231,   3}}, {{134,  82, 236,  13}}, 
+  {{157,  69, 241,  31}}, {{148,  72, 250,  17}}, {{227,   3, 147,  75}}, 
+  {{234,  14, 152,  69}}, {{241,  25, 133,  87}}, {{248,  20, 142,  89}}, 
+  {{199,  55, 191, 115}}, {{206,  58, 180, 125}}, {{213,  45, 169, 111}}, 
+  {{220,  32, 162,  97}}, {{118, 109, 246, 173}}, {{127,  96, 253, 163}}, 
+  {{100, 119, 224, 177}}, {{109, 122, 235, 191}}, {{ 82,  89, 218, 149}}, 
+  {{ 91,  84, 209, 155}}, {{ 64,  67, 204, 137}}, {{ 73,  78, 199, 135}}, 
+  {{ 62,   5, 174, 221}}, {{ 55,   8, 165, 211}}, {{ 44,  31, 184, 193}}, 
+  {{ 37,  18, 179, 207}}, {{ 26,  49, 130, 229}}, {{ 19,  60, 137, 235}}, 
+  {{  8,  43, 148, 249}}, {{  1,  38, 159, 247}}, {{230, 189,  70,  77}}, 
+  {{239, 176,  77,  67}}, {{244, 167,  80,  81}}, {{253, 170,  91,  95}}, 
+  {{194, 137, 106, 117}}, {{203, 132,  97, 123}}, {{208, 147, 124, 105}}, 
+  {{217, 158, 119, 103}}, {{174, 213,  30,  61}}, {{167, 216,  21,  51}}, 
+  {{188, 207,   8,  33}}, {{181, 194,   3,  47}}, {{138, 225,  50,   5}}, 
+  {{131, 236,  57,  11}}, {{152, 251,  36,  25}}, {{145, 246,  47,  23}}, 
+  {{ 77, 214, 141, 118}}, {{ 68, 219, 134, 120}}, {{ 95, 204, 155, 106}}, 
+  {{ 86, 193, 144, 100}}, {{105, 226, 161,  78}}, {{ 96, 239, 170,  64}}, 
+  {{123, 248, 183,  82}}, {{114, 245, 188,  92}}, {{  5, 190, 213,   6}}, 
+  {{ 12, 179, 222,   8}}, {{ 23, 164, 195,  26}}, {{ 30, 169, 200,  20}}, 
+  {{ 33, 138, 249,  62}}, {{ 40, 135, 242,  48}}, {{ 51, 144, 239,  34}}, 
+  {{ 58, 157, 228,  44}}, {{221,   6,  61, 150}}, {{212,  11,  54, 152}}, 
+  {{207,  28,  43, 138}}, {{198,  17,  32, 132}}, {{249,  50,  17, 174}}, 
+  {{240,  63,  26, 160}}, {{235,  40,   7, 178}}, {{226,  37,  12, 188}}, 
+  {{149, 110, 101, 230}}, {{156,  99, 110, 232}}, {{135, 116, 115, 250}}, 
+  {{142, 121, 120, 244}}, {{177,  90,  73, 222}}, {{184,  87,  66, 208}}, 
+  {{163,  64,  95, 194}}, {{170,  77,  84, 204}}, {{236, 218, 247,  65}}, 
+  {{229, 215, 252,  79}}, {{254, 192, 225,  93}}, {{247, 205, 234,  83}}, 
+  {{200, 238, 219, 121}}, {{193, 227, 208, 119}}, {{218, 244, 205, 101}}, 
+  {{211, 249, 198, 107}}, {{164, 178, 175,  49}}, {{173, 191, 164,  63}}, 
+  {{182, 168, 185,  45}}, {{191, 165, 178,  35}}, {{128, 134, 131,   9}}, 
+  {{137, 139, 136,   7}}, {{146, 156, 149,  21}}, {{155, 145, 158,  27}}, 
+  {{124,  10,  71, 161}}, {{117,   7,  76, 175}}, {{110,  16,  81, 189}}, 
+  {{103,  29,  90, 179}}, {{ 88,  62, 107, 153}}, {{ 81,  51,  96, 151}}, 
+  {{ 74,  36, 125, 133}}, {{ 67,  41, 118, 139}}, {{ 52,  98,  31, 209}}, 
+  {{ 61, 111,  20, 223}}, {{ 38, 120,   9, 205}}, {{ 47, 117,   2, 195}}, 
+  {{ 16,  86,  51, 233}}, {{ 25,  91,  56, 231}}, {{  2,  76,  37, 245}}, 
+  {{ 11,  65,  46, 251}}, {{215,  97, 140, 154}}, {{222, 108, 135, 148}}, 
+  {{197, 123, 154, 134}}, {{204, 118, 145, 136}}, {{243,  85, 160, 162}}, 
+  {{250,  88, 171, 172}}, {{225,  79, 182, 190}}, {{232,  66, 189, 176}}, 
+  {{159,   9, 212, 234}}, {{150,   4, 223, 228}}, {{141,  19, 194, 246}}, 
+  {{132,  30, 201, 248}}, {{187,  61, 248, 210}}, {{178,  48, 243, 220}}, 
+  {{169,  39, 238, 206}}, {{160,  42, 229, 192}}, {{ 71, 177,  60, 122}}, 
+  {{ 78, 188,  55, 116}}, {{ 85, 171,  42, 102}}, {{ 92, 166,  33, 104}}, 
+  {{ 99, 133,  16,  66}}, {{106, 136,  27,  76}}, {{113, 159,   6,  94}}, 
+  {{120, 146,  13,  80}}, {{ 15, 217, 100,  10}}, {{  6, 212, 111,   4}}, 
+  {{ 29, 195, 114,  22}}, {{ 20, 206, 121,  24}}, {{ 43, 237,  72,  50}}, 
+  {{ 34, 224,  67,  60}}, {{ 57, 247,  94,  46}}, {{ 48, 250,  85,  32}}, 
+  {{154, 183,   1, 236}}, {{147, 186,  10, 226}}, {{136, 173,  23, 240}}, 
+  {{129, 160,  28, 254}}, {{190, 131,  45, 212}}, {{183, 142,  38, 218}}, 
+  {{172, 153,  59, 200}}, {{165, 148,  48, 198}}, {{210, 223,  89, 156}}, 
+  {{219, 210,  82, 146}}, {{192, 197,  79, 128}}, {{201, 200,  68, 142}}, 
+  {{246, 235, 117, 164}}, {{255, 230, 126, 170}}, {{228, 241,  99, 184}}, 
+  {{237, 252, 104, 182}}, {{ 10, 103, 177,  12}}, {{  3, 106, 186,   2}}, 
+  {{ 24, 125, 167,  16}}, {{ 17, 112, 172,  30}}, {{ 46,  83, 157,  52}}, 
+  {{ 39,  94, 150,  58}}, {{ 60,  73, 139,  40}}, {{ 53,  68, 128,  38}}, 
+  {{ 66,  15, 233, 124}}, {{ 75,   2, 226, 114}}, {{ 80,  21, 255,  96}}, 
+  {{ 89,  24, 244, 110}}, {{102,  59, 197,  68}}, {{111,  54, 206,  74}}, 
+  {{116,  33, 211,  88}}, {{125,  44, 216,  86}}, {{161,  12, 122,  55}}, 
+  {{168,   1, 113,  57}}, {{179,  22, 108,  43}}, {{186,  27, 103,  37}}, 
+  {{133,  56,  86,  15}}, {{140,  53,  93,   1}}, {{151,  34,  64,  19}}, 
+  {{158,  47,  75,  29}}, {{233, 100,  34,  71}}, {{224, 105,  41,  73}}, 
+  {{251, 126,  52,  91}}, {{242, 115,  63,  85}}, {{205,  80,  14, 127}}, 
+  {{196,  93,   5, 113}}, {{223,  74,  24,  99}}, {{214,  71,  19, 109}}, 
+  {{ 49, 220, 202, 215}}, {{ 56, 209, 193, 217}}, {{ 35, 198, 220, 203}}, 
+  {{ 42, 203, 215, 197}}, {{ 21, 232, 230, 239}}, {{ 28, 229, 237, 225}}, 
+  {{  7, 242, 240, 243}}, {{ 14, 255, 251, 253}}, {{121, 180, 146, 167}}, 
+  {{112, 185, 153, 169}}, {{107, 174, 132, 187}}, {{ 98, 163, 143, 181}}, 
+  {{ 93, 128, 190, 159}}, {{ 84, 141, 181, 145}}, {{ 79, 154, 168, 131}}, 
+  {{ 70, 151, 163, 141}}, 
+ },
+};
+
+const int xrcon[30] = {
+  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 
+  0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 
+  0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 
+};
+
+const word8 xS[256] = {
+   99, 124, 119, 123, 242, 107, 111, 197,  48,   1, 103,  43, 254, 215, 
+  171, 118, 202, 130, 201, 125, 250,  89,  71, 240, 173, 212, 162, 175, 
+  156, 164, 114, 192, 183, 253, 147,  38,  54,  63, 247, 204,  52, 165, 
+  229, 241, 113, 216,  49,  21,   4, 199,  35, 195,  24, 150,   5, 154, 
+    7,  18, 128, 226, 235,  39, 178, 117,   9, 131,  44,  26,  27, 110, 
+   90, 160,  82,  59, 214, 179,  41, 227,  47, 132,  83, 209,   0, 237, 
+   32, 252, 177,  91, 106, 203, 190,  57,  74,  76,  88, 207, 208, 239, 
+  170, 251,  67,  77,  51, 133,  69, 249,   2, 127,  80,  60, 159, 168, 
+   81, 163,  64, 143, 146, 157,  56, 245, 188, 182, 218,  33,  16, 255, 
+  243, 210, 205,  12,  19, 236,  95, 151,  68,  23, 196, 167, 126,  61, 
+  100,  93,  25, 115,  96, 129,  79, 220,  34,  42, 144, 136,  70, 238, 
+  184,  20, 222,  94,  11, 219, 224,  50,  58,  10,  73,   6,  36,  92, 
+  194, 211, 172,  98, 145, 149, 228, 121, 231, 200,  55, 109, 141, 213, 
+   78, 169, 108,  86, 244, 234, 101, 122, 174,   8, 186, 120,  37,  46, 
+   28, 166, 180, 198, 232, 221, 116,  31,  75, 189, 139, 138, 112,  62, 
+  181, 102,  72,   3, 246,  14,  97,  53,  87, 185, 134, 193,  29, 158, 
+  225, 248, 152,  17, 105, 217, 142, 148, 155,  30, 135, 233, 206,  85, 
+   40, 223, 140, 161, 137,  13, 191, 230,  66, 104,  65, 153,  45,  15, 
+  176,  84, 187,  22, 
+};
+
+const word8 xSi[256] = {
+   82,   9, 106, 213,  48,  54, 165,  56, 191,  64, 163, 158, 129, 243, 
+  215, 251, 124, 227,  57, 130, 155,  47, 255, 135,  52, 142,  67,  68, 
+  196, 222, 233, 203,  84, 123, 148,  50, 166, 194,  35,  61, 238,  76, 
+  149,  11,  66, 250, 195,  78,   8,  46, 161, 102,  40, 217,  36, 178, 
+  118,  91, 162,  73, 109, 139, 209,  37, 114, 248, 246, 100, 134, 104, 
+  152,  22, 212, 164,  92, 204,  93, 101, 182, 146, 108, 112,  72,  80, 
+  253, 237, 185, 218,  94,  21,  70,  87, 167, 141, 157, 132, 144, 216, 
+  171,   0, 140, 188, 211,  10, 247, 228,  88,   5, 184, 179,  69,   6, 
+  208,  44,  30, 143, 202,  63,  15,   2, 193, 175, 189,   3,   1,  19, 
+  138, 107,  58, 145,  17,  65,  79, 103, 220, 234, 151, 242, 207, 206, 
+  240, 180, 230, 115, 150, 172, 116,  34, 231, 173,  53, 133, 226, 249, 
+   55, 232,  28, 117, 223, 110,  71, 241,  26, 113,  29,  41, 197, 137, 
+  111, 183,  98,  14, 170,  24, 190,  27, 252,  86,  62,  75, 198, 210, 
+  121,  32, 154, 219, 192, 254, 120, 205,  90, 244,  31, 221, 168,  51, 
+  136,   7, 199,  49, 177,  18,  16,  89,  39, 128, 236,  95,  96,  81, 
+  127, 169,  25, 181,  74,  13,  45, 229, 122, 159, 147, 201, 156, 239, 
+  160, 224,  59,  77, 174,  42, 245, 176, 200, 235, 187,  60, 131,  83, 
+  153,  97,  23,  43,   4, 126, 186, 119, 214,  38, 225, 105,  20,  99, 
+   85,  33,  12, 125, 
+};
+
diff --git a/src/aes/ao_aes_tables.h b/src/aes/ao_aes_tables.h
new file mode 100644 (file)
index 0000000..73bcf3f
--- /dev/null
@@ -0,0 +1,10 @@
+/* Copyright (C) 2000-2009 Peter Selinger.
+   This file is part of ccrypt. It is free software and it is covered
+   by the GNU general public license. See the file COPYING for details. */
+
+extern const word8x4 M0[4][256];
+extern const word8x4 M1[4][256];
+extern const int xrcon[30];
+extern const word8 xS[256];
+extern const word8 xSi[256];
+
diff --git a/src/altitude.h b/src/altitude.h
deleted file mode 100644 (file)
index 5225df4..0000000
+++ /dev/null
@@ -1,2048 +0,0 @@
-       15837,  /*  10.56 kPa 0 count */
-       15804,  /*  10.61 kPa 1 count */
-       15772,  /*  10.66 kPa 2 count */
-       15740,  /*  10.72 kPa 3 count */
-       15708,  /*  10.77 kPa 4 count */
-       15676,  /*  10.83 kPa 5 count */
-       15644,  /*  10.88 kPa 6 count */
-       15613,  /*  10.94 kPa 7 count */
-       15581,  /*  10.99 kPa 8 count */
-       15550,  /*  11.04 kPa 9 count */
-       15519,  /*  11.10 kPa 10 count */
-       15488,  /*  11.15 kPa 11 count */
-       15457,  /*  11.21 kPa 12 count */
-       15426,  /*  11.26 kPa 13 count */
-       15396,  /*  11.32 kPa 14 count */
-       15366,  /*  11.37 kPa 15 count */
-       15335,  /*  11.42 kPa 16 count */
-       15305,  /*  11.48 kPa 17 count */
-       15275,  /*  11.53 kPa 18 count */
-       15246,  /*  11.59 kPa 19 count */
-       15216,  /*  11.64 kPa 20 count */
-       15187,  /*  11.70 kPa 21 count */
-       15157,  /*  11.75 kPa 22 count */
-       15128,  /*  11.80 kPa 23 count */
-       15099,  /*  11.86 kPa 24 count */
-       15070,  /*  11.91 kPa 25 count */
-       15041,  /*  11.97 kPa 26 count */
-       15012,  /*  12.02 kPa 27 count */
-       14984,  /*  12.08 kPa 28 count */
-       14955,  /*  12.13 kPa 29 count */
-       14927,  /*  12.18 kPa 30 count */
-       14899,  /*  12.24 kPa 31 count */
-       14871,  /*  12.29 kPa 32 count */
-       14843,  /*  12.35 kPa 33 count */
-       14815,  /*  12.40 kPa 34 count */
-       14787,  /*  12.46 kPa 35 count */
-       14760,  /*  12.51 kPa 36 count */
-       14732,  /*  12.56 kPa 37 count */
-       14705,  /*  12.62 kPa 38 count */
-       14678,  /*  12.67 kPa 39 count */
-       14651,  /*  12.73 kPa 40 count */
-       14624,  /*  12.78 kPa 41 count */
-       14597,  /*  12.84 kPa 42 count */
-       14570,  /*  12.89 kPa 43 count */
-       14543,  /*  12.94 kPa 44 count */
-       14517,  /*  13.00 kPa 45 count */
-       14490,  /*  13.05 kPa 46 count */
-       14464,  /*  13.11 kPa 47 count */
-       14438,  /*  13.16 kPa 48 count */
-       14412,  /*  13.22 kPa 49 count */
-       14386,  /*  13.27 kPa 50 count */
-       14360,  /*  13.32 kPa 51 count */
-       14334,  /*  13.38 kPa 52 count */
-       14308,  /*  13.43 kPa 53 count */
-       14283,  /*  13.49 kPa 54 count */
-       14257,  /*  13.54 kPa 55 count */
-       14232,  /*  13.60 kPa 56 count */
-       14207,  /*  13.65 kPa 57 count */
-       14182,  /*  13.70 kPa 58 count */
-       14156,  /*  13.76 kPa 59 count */
-       14132,  /*  13.81 kPa 60 count */
-       14107,  /*  13.87 kPa 61 count */
-       14082,  /*  13.92 kPa 62 count */
-       14057,  /*  13.98 kPa 63 count */
-       14033,  /*  14.03 kPa 64 count */
-       14008,  /*  14.08 kPa 65 count */
-       13984,  /*  14.14 kPa 66 count */
-       13959,  /*  14.19 kPa 67 count */
-       13935,  /*  14.25 kPa 68 count */
-       13911,  /*  14.30 kPa 69 count */
-       13887,  /*  14.36 kPa 70 count */
-       13863,  /*  14.41 kPa 71 count */
-       13839,  /*  14.46 kPa 72 count */
-       13816,  /*  14.52 kPa 73 count */
-       13792,  /*  14.57 kPa 74 count */
-       13768,  /*  14.63 kPa 75 count */
-       13745,  /*  14.68 kPa 76 count */
-       13721,  /*  14.74 kPa 77 count */
-       13698,  /*  14.79 kPa 78 count */
-       13675,  /*  14.84 kPa 79 count */
-       13652,  /*  14.90 kPa 80 count */
-       13629,  /*  14.95 kPa 81 count */
-       13606,  /*  15.01 kPa 82 count */
-       13583,  /*  15.06 kPa 83 count */
-       13560,  /*  15.12 kPa 84 count */
-       13537,  /*  15.17 kPa 85 count */
-       13515,  /*  15.22 kPa 86 count */
-       13492,  /*  15.28 kPa 87 count */
-       13470,  /*  15.33 kPa 88 count */
-       13447,  /*  15.39 kPa 89 count */
-       13425,  /*  15.44 kPa 90 count */
-       13403,  /*  15.50 kPa 91 count */
-       13380,  /*  15.55 kPa 92 count */
-       13358,  /*  15.60 kPa 93 count */
-       13336,  /*  15.66 kPa 94 count */
-       13314,  /*  15.71 kPa 95 count */
-       13292,  /*  15.77 kPa 96 count */
-       13271,  /*  15.82 kPa 97 count */
-       13249,  /*  15.87 kPa 98 count */
-       13227,  /*  15.93 kPa 99 count */
-       13206,  /*  15.98 kPa 100 count */
-       13184,  /*  16.04 kPa 101 count */
-       13163,  /*  16.09 kPa 102 count */
-       13141,  /*  16.15 kPa 103 count */
-       13120,  /*  16.20 kPa 104 count */
-       13099,  /*  16.25 kPa 105 count */
-       13078,  /*  16.31 kPa 106 count */
-       13057,  /*  16.36 kPa 107 count */
-       13036,  /*  16.42 kPa 108 count */
-       13015,  /*  16.47 kPa 109 count */
-       12994,  /*  16.53 kPa 110 count */
-       12973,  /*  16.58 kPa 111 count */
-       12952,  /*  16.63 kPa 112 count */
-       12932,  /*  16.69 kPa 113 count */
-       12911,  /*  16.74 kPa 114 count */
-       12891,  /*  16.80 kPa 115 count */
-       12870,  /*  16.85 kPa 116 count */
-       12850,  /*  16.91 kPa 117 count */
-       12829,  /*  16.96 kPa 118 count */
-       12809,  /*  17.01 kPa 119 count */
-       12789,  /*  17.07 kPa 120 count */
-       12769,  /*  17.12 kPa 121 count */
-       12749,  /*  17.18 kPa 122 count */
-       12729,  /*  17.23 kPa 123 count */
-       12709,  /*  17.29 kPa 124 count */
-       12689,  /*  17.34 kPa 125 count */
-       12669,  /*  17.39 kPa 126 count */
-       12649,  /*  17.45 kPa 127 count */
-       12630,  /*  17.50 kPa 128 count */
-       12610,  /*  17.56 kPa 129 count */
-       12590,  /*  17.61 kPa 130 count */
-       12571,  /*  17.67 kPa 131 count */
-       12551,  /*  17.72 kPa 132 count */
-       12532,  /*  17.77 kPa 133 count */
-       12513,  /*  17.83 kPa 134 count */
-       12493,  /*  17.88 kPa 135 count */
-       12474,  /*  17.94 kPa 136 count */
-       12455,  /*  17.99 kPa 137 count */
-       12436,  /*  18.05 kPa 138 count */
-       12417,  /*  18.10 kPa 139 count */
-       12398,  /*  18.15 kPa 140 count */
-       12379,  /*  18.21 kPa 141 count */
-       12360,  /*  18.26 kPa 142 count */
-       12341,  /*  18.32 kPa 143 count */
-       12323,  /*  18.37 kPa 144 count */
-       12304,  /*  18.43 kPa 145 count */
-       12285,  /*  18.48 kPa 146 count */
-       12267,  /*  18.53 kPa 147 count */
-       12248,  /*  18.59 kPa 148 count */
-       12230,  /*  18.64 kPa 149 count */
-       12211,  /*  18.70 kPa 150 count */
-       12193,  /*  18.75 kPa 151 count */
-       12174,  /*  18.81 kPa 152 count */
-       12156,  /*  18.86 kPa 153 count */
-       12138,  /*  18.91 kPa 154 count */
-       12120,  /*  18.97 kPa 155 count */
-       12102,  /*  19.02 kPa 156 count */
-       12084,  /*  19.08 kPa 157 count */
-       12065,  /*  19.13 kPa 158 count */
-       12048,  /*  19.19 kPa 159 count */
-       12030,  /*  19.24 kPa 160 count */
-       12012,  /*  19.29 kPa 161 count */
-       11994,  /*  19.35 kPa 162 count */
-       11976,  /*  19.40 kPa 163 count */
-       11958,  /*  19.46 kPa 164 count */
-       11941,  /*  19.51 kPa 165 count */
-       11923,  /*  19.57 kPa 166 count */
-       11906,  /*  19.62 kPa 167 count */
-       11888,  /*  19.67 kPa 168 count */
-       11871,  /*  19.73 kPa 169 count */
-       11853,  /*  19.78 kPa 170 count */
-       11836,  /*  19.84 kPa 171 count */
-       11818,  /*  19.89 kPa 172 count */
-       11801,  /*  19.95 kPa 173 count */
-       11784,  /*  20.00 kPa 174 count */
-       11767,  /*  20.05 kPa 175 count */
-       11750,  /*  20.11 kPa 176 count */
-       11733,  /*  20.16 kPa 177 count */
-       11715,  /*  20.22 kPa 178 count */
-       11698,  /*  20.27 kPa 179 count */
-       11682,  /*  20.33 kPa 180 count */
-       11665,  /*  20.38 kPa 181 count */
-       11648,  /*  20.43 kPa 182 count */
-       11631,  /*  20.49 kPa 183 count */
-       11614,  /*  20.54 kPa 184 count */
-       11597,  /*  20.60 kPa 185 count */
-       11581,  /*  20.65 kPa 186 count */
-       11564,  /*  20.71 kPa 187 count */
-       11547,  /*  20.76 kPa 188 count */
-       11531,  /*  20.81 kPa 189 count */
-       11514,  /*  20.87 kPa 190 count */
-       11498,  /*  20.92 kPa 191 count */
-       11481,  /*  20.98 kPa 192 count */
-       11465,  /*  21.03 kPa 193 count */
-       11449,  /*  21.09 kPa 194 count */
-       11432,  /*  21.14 kPa 195 count */
-       11416,  /*  21.19 kPa 196 count */
-       11400,  /*  21.25 kPa 197 count */
-       11384,  /*  21.30 kPa 198 count */
-       11368,  /*  21.36 kPa 199 count */
-       11352,  /*  21.41 kPa 200 count */
-       11336,  /*  21.47 kPa 201 count */
-       11319,  /*  21.52 kPa 202 count */
-       11304,  /*  21.57 kPa 203 count */
-       11288,  /*  21.63 kPa 204 count */
-       11272,  /*  21.68 kPa 205 count */
-       11256,  /*  21.74 kPa 206 count */
-       11240,  /*  21.79 kPa 207 count */
-       11224,  /*  21.85 kPa 208 count */
-       11208,  /*  21.90 kPa 209 count */
-       11193,  /*  21.95 kPa 210 count */
-       11177,  /*  22.01 kPa 211 count */
-       11162,  /*  22.06 kPa 212 count */
-       11146,  /*  22.12 kPa 213 count */
-       11130,  /*  22.17 kPa 214 count */
-       11115,  /*  22.23 kPa 215 count */
-       11099,  /*  22.28 kPa 216 count */
-       11084,  /*  22.33 kPa 217 count */
-       11069,  /*  22.39 kPa 218 count */
-       11053,  /*  22.44 kPa 219 count */
-       11038,  /*  22.50 kPa 220 count */
-       11023,  /*  22.55 kPa 221 count */
-       11007,  /*  22.61 kPa 222 count */
-       10992,  /*  22.66 kPa 223 count */
-       10977,  /*  22.71 kPa 224 count */
-       10962,  /*  22.77 kPa 225 count */
-       10947,  /*  22.82 kPa 226 count */
-       10932,  /*  22.88 kPa 227 count */
-       10917,  /*  22.93 kPa 228 count */
-       10902,  /*  22.99 kPa 229 count */
-       10887,  /*  23.04 kPa 230 count */
-       10872,  /*  23.09 kPa 231 count */
-       10857,  /*  23.15 kPa 232 count */
-       10842,  /*  23.20 kPa 233 count */
-       10827,  /*  23.26 kPa 234 count */
-       10812,  /*  23.31 kPa 235 count */
-       10797,  /*  23.37 kPa 236 count */
-       10782,  /*  23.42 kPa 237 count */
-       10768,  /*  23.47 kPa 238 count */
-       10753,  /*  23.53 kPa 239 count */
-       10738,  /*  23.58 kPa 240 count */
-       10723,  /*  23.64 kPa 241 count */
-       10709,  /*  23.69 kPa 242 count */
-       10694,  /*  23.75 kPa 243 count */
-       10679,  /*  23.80 kPa 244 count */
-       10665,  /*  23.85 kPa 245 count */
-       10650,  /*  23.91 kPa 246 count */
-       10636,  /*  23.96 kPa 247 count */
-       10621,  /*  24.02 kPa 248 count */
-       10607,  /*  24.07 kPa 249 count */
-       10592,  /*  24.13 kPa 250 count */
-       10578,  /*  24.18 kPa 251 count */
-       10563,  /*  24.23 kPa 252 count */
-       10549,  /*  24.29 kPa 253 count */
-       10535,  /*  24.34 kPa 254 count */
-       10520,  /*  24.40 kPa 255 count */
-       10506,  /*  24.45 kPa 256 count */
-       10492,  /*  24.51 kPa 257 count */
-       10478,  /*  24.56 kPa 258 count */
-       10463,  /*  24.61 kPa 259 count */
-       10449,  /*  24.67 kPa 260 count */
-       10435,  /*  24.72 kPa 261 count */
-       10421,  /*  24.78 kPa 262 count */
-       10407,  /*  24.83 kPa 263 count */
-       10393,  /*  24.89 kPa 264 count */
-       10379,  /*  24.94 kPa 265 count */
-       10364,  /*  24.99 kPa 266 count */
-       10350,  /*  25.05 kPa 267 count */
-       10336,  /*  25.10 kPa 268 count */
-       10322,  /*  25.16 kPa 269 count */
-       10309,  /*  25.21 kPa 270 count */
-       10295,  /*  25.27 kPa 271 count */
-       10281,  /*  25.32 kPa 272 count */
-       10267,  /*  25.37 kPa 273 count */
-       10253,  /*  25.43 kPa 274 count */
-       10239,  /*  25.48 kPa 275 count */
-       10225,  /*  25.54 kPa 276 count */
-       10212,  /*  25.59 kPa 277 count */
-       10198,  /*  25.65 kPa 278 count */
-       10184,  /*  25.70 kPa 279 count */
-       10170,  /*  25.75 kPa 280 count */
-       10157,  /*  25.81 kPa 281 count */
-       10143,  /*  25.86 kPa 282 count */
-       10129,  /*  25.92 kPa 283 count */
-       10116,  /*  25.97 kPa 284 count */
-       10102,  /*  26.03 kPa 285 count */
-       10089,  /*  26.08 kPa 286 count */
-       10075,  /*  26.13 kPa 287 count */
-       10062,  /*  26.19 kPa 288 count */
-       10048,  /*  26.24 kPa 289 count */
-       10035,  /*  26.30 kPa 290 count */
-       10021,  /*  26.35 kPa 291 count */
-       10008,  /*  26.41 kPa 292 count */
-       9994,   /*  26.46 kPa 293 count */
-       9981,   /*  26.51 kPa 294 count */
-       9967,   /*  26.57 kPa 295 count */
-       9954,   /*  26.62 kPa 296 count */
-       9941,   /*  26.68 kPa 297 count */
-       9928,   /*  26.73 kPa 298 count */
-       9914,   /*  26.79 kPa 299 count */
-       9901,   /*  26.84 kPa 300 count */
-       9888,   /*  26.89 kPa 301 count */
-       9875,   /*  26.95 kPa 302 count */
-       9861,   /*  27.00 kPa 303 count */
-       9848,   /*  27.06 kPa 304 count */
-       9835,   /*  27.11 kPa 305 count */
-       9822,   /*  27.17 kPa 306 count */
-       9809,   /*  27.22 kPa 307 count */
-       9796,   /*  27.27 kPa 308 count */
-       9783,   /*  27.33 kPa 309 count */
-       9770,   /*  27.38 kPa 310 count */
-       9757,   /*  27.44 kPa 311 count */
-       9744,   /*  27.49 kPa 312 count */
-       9731,   /*  27.55 kPa 313 count */
-       9718,   /*  27.60 kPa 314 count */
-       9705,   /*  27.65 kPa 315 count */
-       9692,   /*  27.71 kPa 316 count */
-       9679,   /*  27.76 kPa 317 count */
-       9666,   /*  27.82 kPa 318 count */
-       9653,   /*  27.87 kPa 319 count */
-       9640,   /*  27.93 kPa 320 count */
-       9627,   /*  27.98 kPa 321 count */
-       9615,   /*  28.03 kPa 322 count */
-       9602,   /*  28.09 kPa 323 count */
-       9589,   /*  28.14 kPa 324 count */
-       9576,   /*  28.20 kPa 325 count */
-       9564,   /*  28.25 kPa 326 count */
-       9551,   /*  28.31 kPa 327 count */
-       9538,   /*  28.36 kPa 328 count */
-       9526,   /*  28.41 kPa 329 count */
-       9513,   /*  28.47 kPa 330 count */
-       9500,   /*  28.52 kPa 331 count */
-       9488,   /*  28.58 kPa 332 count */
-       9475,   /*  28.63 kPa 333 count */
-       9463,   /*  28.69 kPa 334 count */
-       9450,   /*  28.74 kPa 335 count */
-       9438,   /*  28.79 kPa 336 count */
-       9425,   /*  28.85 kPa 337 count */
-       9413,   /*  28.90 kPa 338 count */
-       9400,   /*  28.96 kPa 339 count */
-       9388,   /*  29.01 kPa 340 count */
-       9375,   /*  29.07 kPa 341 count */
-       9363,   /*  29.12 kPa 342 count */
-       9350,   /*  29.17 kPa 343 count */
-       9338,   /*  29.23 kPa 344 count */
-       9326,   /*  29.28 kPa 345 count */
-       9313,   /*  29.34 kPa 346 count */
-       9301,   /*  29.39 kPa 347 count */
-       9289,   /*  29.44 kPa 348 count */
-       9276,   /*  29.50 kPa 349 count */
-       9264,   /*  29.55 kPa 350 count */
-       9252,   /*  29.61 kPa 351 count */
-       9240,   /*  29.66 kPa 352 count */
-       9227,   /*  29.72 kPa 353 count */
-       9215,   /*  29.77 kPa 354 count */
-       9203,   /*  29.82 kPa 355 count */
-       9191,   /*  29.88 kPa 356 count */
-       9179,   /*  29.93 kPa 357 count */
-       9167,   /*  29.99 kPa 358 count */
-       9155,   /*  30.04 kPa 359 count */
-       9142,   /*  30.10 kPa 360 count */
-       9130,   /*  30.15 kPa 361 count */
-       9118,   /*  30.20 kPa 362 count */
-       9106,   /*  30.26 kPa 363 count */
-       9094,   /*  30.31 kPa 364 count */
-       9082,   /*  30.37 kPa 365 count */
-       9070,   /*  30.42 kPa 366 count */
-       9058,   /*  30.48 kPa 367 count */
-       9046,   /*  30.53 kPa 368 count */
-       9035,   /*  30.58 kPa 369 count */
-       9023,   /*  30.64 kPa 370 count */
-       9011,   /*  30.69 kPa 371 count */
-       8999,   /*  30.75 kPa 372 count */
-       8987,   /*  30.80 kPa 373 count */
-       8975,   /*  30.86 kPa 374 count */
-       8963,   /*  30.91 kPa 375 count */
-       8952,   /*  30.96 kPa 376 count */
-       8940,   /*  31.02 kPa 377 count */
-       8928,   /*  31.07 kPa 378 count */
-       8916,   /*  31.13 kPa 379 count */
-       8904,   /*  31.18 kPa 380 count */
-       8893,   /*  31.24 kPa 381 count */
-       8881,   /*  31.29 kPa 382 count */
-       8869,   /*  31.34 kPa 383 count */
-       8858,   /*  31.40 kPa 384 count */
-       8846,   /*  31.45 kPa 385 count */
-       8834,   /*  31.51 kPa 386 count */
-       8823,   /*  31.56 kPa 387 count */
-       8811,   /*  31.62 kPa 388 count */
-       8800,   /*  31.67 kPa 389 count */
-       8788,   /*  31.72 kPa 390 count */
-       8776,   /*  31.78 kPa 391 count */
-       8765,   /*  31.83 kPa 392 count */
-       8753,   /*  31.89 kPa 393 count */
-       8742,   /*  31.94 kPa 394 count */
-       8730,   /*  32.00 kPa 395 count */
-       8719,   /*  32.05 kPa 396 count */
-       8707,   /*  32.10 kPa 397 count */
-       8696,   /*  32.16 kPa 398 count */
-       8684,   /*  32.21 kPa 399 count */
-       8673,   /*  32.27 kPa 400 count */
-       8662,   /*  32.32 kPa 401 count */
-       8650,   /*  32.38 kPa 402 count */
-       8639,   /*  32.43 kPa 403 count */
-       8628,   /*  32.48 kPa 404 count */
-       8616,   /*  32.54 kPa 405 count */
-       8605,   /*  32.59 kPa 406 count */
-       8594,   /*  32.65 kPa 407 count */
-       8582,   /*  32.70 kPa 408 count */
-       8571,   /*  32.76 kPa 409 count */
-       8560,   /*  32.81 kPa 410 count */
-       8548,   /*  32.86 kPa 411 count */
-       8537,   /*  32.92 kPa 412 count */
-       8526,   /*  32.97 kPa 413 count */
-       8515,   /*  33.03 kPa 414 count */
-       8504,   /*  33.08 kPa 415 count */
-       8492,   /*  33.14 kPa 416 count */
-       8481,   /*  33.19 kPa 417 count */
-       8470,   /*  33.24 kPa 418 count */
-       8459,   /*  33.30 kPa 419 count */
-       8448,   /*  33.35 kPa 420 count */
-       8437,   /*  33.41 kPa 421 count */
-       8426,   /*  33.46 kPa 422 count */
-       8415,   /*  33.52 kPa 423 count */
-       8403,   /*  33.57 kPa 424 count */
-       8392,   /*  33.62 kPa 425 count */
-       8381,   /*  33.68 kPa 426 count */
-       8370,   /*  33.73 kPa 427 count */
-       8359,   /*  33.79 kPa 428 count */
-       8348,   /*  33.84 kPa 429 count */
-       8337,   /*  33.90 kPa 430 count */
-       8326,   /*  33.95 kPa 431 count */
-       8316,   /*  34.00 kPa 432 count */
-       8305,   /*  34.06 kPa 433 count */
-       8294,   /*  34.11 kPa 434 count */
-       8283,   /*  34.17 kPa 435 count */
-       8272,   /*  34.22 kPa 436 count */
-       8261,   /*  34.28 kPa 437 count */
-       8250,   /*  34.33 kPa 438 count */
-       8239,   /*  34.38 kPa 439 count */
-       8228,   /*  34.44 kPa 440 count */
-       8218,   /*  34.49 kPa 441 count */
-       8207,   /*  34.55 kPa 442 count */
-       8196,   /*  34.60 kPa 443 count */
-       8185,   /*  34.66 kPa 444 count */
-       8175,   /*  34.71 kPa 445 count */
-       8164,   /*  34.76 kPa 446 count */
-       8153,   /*  34.82 kPa 447 count */
-       8142,   /*  34.87 kPa 448 count */
-       8132,   /*  34.93 kPa 449 count */
-       8121,   /*  34.98 kPa 450 count */
-       8110,   /*  35.04 kPa 451 count */
-       8100,   /*  35.09 kPa 452 count */
-       8089,   /*  35.14 kPa 453 count */
-       8078,   /*  35.20 kPa 454 count */
-       8068,   /*  35.25 kPa 455 count */
-       8057,   /*  35.31 kPa 456 count */
-       8046,   /*  35.36 kPa 457 count */
-       8036,   /*  35.42 kPa 458 count */
-       8025,   /*  35.47 kPa 459 count */
-       8015,   /*  35.52 kPa 460 count */
-       8004,   /*  35.58 kPa 461 count */
-       7994,   /*  35.63 kPa 462 count */
-       7983,   /*  35.69 kPa 463 count */
-       7973,   /*  35.74 kPa 464 count */
-       7962,   /*  35.80 kPa 465 count */
-       7952,   /*  35.85 kPa 466 count */
-       7941,   /*  35.90 kPa 467 count */
-       7931,   /*  35.96 kPa 468 count */
-       7920,   /*  36.01 kPa 469 count */
-       7910,   /*  36.07 kPa 470 count */
-       7899,   /*  36.12 kPa 471 count */
-       7889,   /*  36.18 kPa 472 count */
-       7879,   /*  36.23 kPa 473 count */
-       7868,   /*  36.28 kPa 474 count */
-       7858,   /*  36.34 kPa 475 count */
-       7847,   /*  36.39 kPa 476 count */
-       7837,   /*  36.45 kPa 477 count */
-       7827,   /*  36.50 kPa 478 count */
-       7816,   /*  36.56 kPa 479 count */
-       7806,   /*  36.61 kPa 480 count */
-       7796,   /*  36.66 kPa 481 count */
-       7785,   /*  36.72 kPa 482 count */
-       7775,   /*  36.77 kPa 483 count */
-       7765,   /*  36.83 kPa 484 count */
-       7755,   /*  36.88 kPa 485 count */
-       7744,   /*  36.94 kPa 486 count */
-       7734,   /*  36.99 kPa 487 count */
-       7724,   /*  37.04 kPa 488 count */
-       7714,   /*  37.10 kPa 489 count */
-       7704,   /*  37.15 kPa 490 count */
-       7693,   /*  37.21 kPa 491 count */
-       7683,   /*  37.26 kPa 492 count */
-       7673,   /*  37.32 kPa 493 count */
-       7663,   /*  37.37 kPa 494 count */
-       7653,   /*  37.42 kPa 495 count */
-       7643,   /*  37.48 kPa 496 count */
-       7633,   /*  37.53 kPa 497 count */
-       7623,   /*  37.59 kPa 498 count */
-       7613,   /*  37.64 kPa 499 count */
-       7602,   /*  37.70 kPa 500 count */
-       7592,   /*  37.75 kPa 501 count */
-       7582,   /*  37.80 kPa 502 count */
-       7572,   /*  37.86 kPa 503 count */
-       7562,   /*  37.91 kPa 504 count */
-       7552,   /*  37.97 kPa 505 count */
-       7542,   /*  38.02 kPa 506 count */
-       7532,   /*  38.08 kPa 507 count */
-       7522,   /*  38.13 kPa 508 count */
-       7512,   /*  38.18 kPa 509 count */
-       7502,   /*  38.24 kPa 510 count */
-       7492,   /*  38.29 kPa 511 count */
-       7483,   /*  38.35 kPa 512 count */
-       7473,   /*  38.40 kPa 513 count */
-       7463,   /*  38.46 kPa 514 count */
-       7453,   /*  38.51 kPa 515 count */
-       7443,   /*  38.56 kPa 516 count */
-       7433,   /*  38.62 kPa 517 count */
-       7423,   /*  38.67 kPa 518 count */
-       7413,   /*  38.73 kPa 519 count */
-       7403,   /*  38.78 kPa 520 count */
-       7394,   /*  38.84 kPa 521 count */
-       7384,   /*  38.89 kPa 522 count */
-       7374,   /*  38.94 kPa 523 count */
-       7364,   /*  39.00 kPa 524 count */
-       7354,   /*  39.05 kPa 525 count */
-       7345,   /*  39.11 kPa 526 count */
-       7335,   /*  39.16 kPa 527 count */
-       7325,   /*  39.22 kPa 528 count */
-       7315,   /*  39.27 kPa 529 count */
-       7306,   /*  39.32 kPa 530 count */
-       7296,   /*  39.38 kPa 531 count */
-       7286,   /*  39.43 kPa 532 count */
-       7277,   /*  39.49 kPa 533 count */
-       7267,   /*  39.54 kPa 534 count */
-       7257,   /*  39.60 kPa 535 count */
-       7248,   /*  39.65 kPa 536 count */
-       7238,   /*  39.70 kPa 537 count */
-       7228,   /*  39.76 kPa 538 count */
-       7219,   /*  39.81 kPa 539 count */
-       7209,   /*  39.87 kPa 540 count */
-       7199,   /*  39.92 kPa 541 count */
-       7190,   /*  39.98 kPa 542 count */
-       7180,   /*  40.03 kPa 543 count */
-       7171,   /*  40.08 kPa 544 count */
-       7161,   /*  40.14 kPa 545 count */
-       7152,   /*  40.19 kPa 546 count */
-       7142,   /*  40.25 kPa 547 count */
-       7132,   /*  40.30 kPa 548 count */
-       7123,   /*  40.36 kPa 549 count */
-       7113,   /*  40.41 kPa 550 count */
-       7104,   /*  40.46 kPa 551 count */
-       7094,   /*  40.52 kPa 552 count */
-       7085,   /*  40.57 kPa 553 count */
-       7075,   /*  40.63 kPa 554 count */
-       7066,   /*  40.68 kPa 555 count */
-       7056,   /*  40.74 kPa 556 count */
-       7047,   /*  40.79 kPa 557 count */
-       7038,   /*  40.84 kPa 558 count */
-       7028,   /*  40.90 kPa 559 count */
-       7019,   /*  40.95 kPa 560 count */
-       7009,   /*  41.01 kPa 561 count */
-       7000,   /*  41.06 kPa 562 count */
-       6991,   /*  41.12 kPa 563 count */
-       6981,   /*  41.17 kPa 564 count */
-       6972,   /*  41.22 kPa 565 count */
-       6962,   /*  41.28 kPa 566 count */
-       6953,   /*  41.33 kPa 567 count */
-       6944,   /*  41.39 kPa 568 count */
-       6934,   /*  41.44 kPa 569 count */
-       6925,   /*  41.50 kPa 570 count */
-       6916,   /*  41.55 kPa 571 count */
-       6907,   /*  41.60 kPa 572 count */
-       6897,   /*  41.66 kPa 573 count */
-       6888,   /*  41.71 kPa 574 count */
-       6879,   /*  41.77 kPa 575 count */
-       6869,   /*  41.82 kPa 576 count */
-       6860,   /*  41.88 kPa 577 count */
-       6851,   /*  41.93 kPa 578 count */
-       6842,   /*  41.98 kPa 579 count */
-       6833,   /*  42.04 kPa 580 count */
-       6823,   /*  42.09 kPa 581 count */
-       6814,   /*  42.15 kPa 582 count */
-       6805,   /*  42.20 kPa 583 count */
-       6796,   /*  42.26 kPa 584 count */
-       6787,   /*  42.31 kPa 585 count */
-       6777,   /*  42.36 kPa 586 count */
-       6768,   /*  42.42 kPa 587 count */
-       6759,   /*  42.47 kPa 588 count */
-       6750,   /*  42.53 kPa 589 count */
-       6741,   /*  42.58 kPa 590 count */
-       6732,   /*  42.64 kPa 591 count */
-       6723,   /*  42.69 kPa 592 count */
-       6714,   /*  42.74 kPa 593 count */
-       6705,   /*  42.80 kPa 594 count */
-       6695,   /*  42.85 kPa 595 count */
-       6686,   /*  42.91 kPa 596 count */
-       6677,   /*  42.96 kPa 597 count */
-       6668,   /*  43.01 kPa 598 count */
-       6659,   /*  43.07 kPa 599 count */
-       6650,   /*  43.12 kPa 600 count */
-       6641,   /*  43.18 kPa 601 count */
-       6632,   /*  43.23 kPa 602 count */
-       6623,   /*  43.29 kPa 603 count */
-       6614,   /*  43.34 kPa 604 count */
-       6605,   /*  43.39 kPa 605 count */
-       6596,   /*  43.45 kPa 606 count */
-       6587,   /*  43.50 kPa 607 count */
-       6578,   /*  43.56 kPa 608 count */
-       6569,   /*  43.61 kPa 609 count */
-       6560,   /*  43.67 kPa 610 count */
-       6552,   /*  43.72 kPa 611 count */
-       6543,   /*  43.77 kPa 612 count */
-       6534,   /*  43.83 kPa 613 count */
-       6525,   /*  43.88 kPa 614 count */
-       6516,   /*  43.94 kPa 615 count */
-       6507,   /*  43.99 kPa 616 count */
-       6498,   /*  44.05 kPa 617 count */
-       6489,   /*  44.10 kPa 618 count */
-       6480,   /*  44.15 kPa 619 count */
-       6472,   /*  44.21 kPa 620 count */
-       6463,   /*  44.26 kPa 621 count */
-       6454,   /*  44.32 kPa 622 count */
-       6445,   /*  44.37 kPa 623 count */
-       6436,   /*  44.43 kPa 624 count */
-       6427,   /*  44.48 kPa 625 count */
-       6419,   /*  44.53 kPa 626 count */
-       6410,   /*  44.59 kPa 627 count */
-       6401,   /*  44.64 kPa 628 count */
-       6392,   /*  44.70 kPa 629 count */
-       6384,   /*  44.75 kPa 630 count */
-       6375,   /*  44.81 kPa 631 count */
-       6366,   /*  44.86 kPa 632 count */
-       6357,   /*  44.91 kPa 633 count */
-       6349,   /*  44.97 kPa 634 count */
-       6340,   /*  45.02 kPa 635 count */
-       6331,   /*  45.08 kPa 636 count */
-       6322,   /*  45.13 kPa 637 count */
-       6314,   /*  45.19 kPa 638 count */
-       6305,   /*  45.24 kPa 639 count */
-       6296,   /*  45.29 kPa 640 count */
-       6288,   /*  45.35 kPa 641 count */
-       6279,   /*  45.40 kPa 642 count */
-       6270,   /*  45.46 kPa 643 count */
-       6262,   /*  45.51 kPa 644 count */
-       6253,   /*  45.57 kPa 645 count */
-       6245,   /*  45.62 kPa 646 count */
-       6236,   /*  45.67 kPa 647 count */
-       6227,   /*  45.73 kPa 648 count */
-       6219,   /*  45.78 kPa 649 count */
-       6210,   /*  45.84 kPa 650 count */
-       6202,   /*  45.89 kPa 651 count */
-       6193,   /*  45.95 kPa 652 count */
-       6184,   /*  46.00 kPa 653 count */
-       6176,   /*  46.05 kPa 654 count */
-       6167,   /*  46.11 kPa 655 count */
-       6159,   /*  46.16 kPa 656 count */
-       6150,   /*  46.22 kPa 657 count */
-       6142,   /*  46.27 kPa 658 count */
-       6133,   /*  46.33 kPa 659 count */
-       6125,   /*  46.38 kPa 660 count */
-       6116,   /*  46.43 kPa 661 count */
-       6108,   /*  46.49 kPa 662 count */
-       6099,   /*  46.54 kPa 663 count */
-       6091,   /*  46.60 kPa 664 count */
-       6082,   /*  46.65 kPa 665 count */
-       6074,   /*  46.71 kPa 666 count */
-       6065,   /*  46.76 kPa 667 count */
-       6057,   /*  46.81 kPa 668 count */
-       6048,   /*  46.87 kPa 669 count */
-       6040,   /*  46.92 kPa 670 count */
-       6032,   /*  46.98 kPa 671 count */
-       6023,   /*  47.03 kPa 672 count */
-       6015,   /*  47.09 kPa 673 count */
-       6006,   /*  47.14 kPa 674 count */
-       5998,   /*  47.19 kPa 675 count */
-       5990,   /*  47.25 kPa 676 count */
-       5981,   /*  47.30 kPa 677 count */
-       5973,   /*  47.36 kPa 678 count */
-       5964,   /*  47.41 kPa 679 count */
-       5956,   /*  47.47 kPa 680 count */
-       5948,   /*  47.52 kPa 681 count */
-       5939,   /*  47.57 kPa 682 count */
-       5931,   /*  47.63 kPa 683 count */
-       5923,   /*  47.68 kPa 684 count */
-       5914,   /*  47.74 kPa 685 count */
-       5906,   /*  47.79 kPa 686 count */
-       5898,   /*  47.85 kPa 687 count */
-       5890,   /*  47.90 kPa 688 count */
-       5881,   /*  47.95 kPa 689 count */
-       5873,   /*  48.01 kPa 690 count */
-       5865,   /*  48.06 kPa 691 count */
-       5856,   /*  48.12 kPa 692 count */
-       5848,   /*  48.17 kPa 693 count */
-       5840,   /*  48.23 kPa 694 count */
-       5832,   /*  48.28 kPa 695 count */
-       5823,   /*  48.33 kPa 696 count */
-       5815,   /*  48.39 kPa 697 count */
-       5807,   /*  48.44 kPa 698 count */
-       5799,   /*  48.50 kPa 699 count */
-       5791,   /*  48.55 kPa 700 count */
-       5782,   /*  48.61 kPa 701 count */
-       5774,   /*  48.66 kPa 702 count */
-       5766,   /*  48.71 kPa 703 count */
-       5758,   /*  48.77 kPa 704 count */
-       5750,   /*  48.82 kPa 705 count */
-       5742,   /*  48.88 kPa 706 count */
-       5733,   /*  48.93 kPa 707 count */
-       5725,   /*  48.99 kPa 708 count */
-       5717,   /*  49.04 kPa 709 count */
-       5709,   /*  49.09 kPa 710 count */
-       5701,   /*  49.15 kPa 711 count */
-       5693,   /*  49.20 kPa 712 count */
-       5685,   /*  49.26 kPa 713 count */
-       5677,   /*  49.31 kPa 714 count */
-       5668,   /*  49.37 kPa 715 count */
-       5660,   /*  49.42 kPa 716 count */
-       5652,   /*  49.47 kPa 717 count */
-       5644,   /*  49.53 kPa 718 count */
-       5636,   /*  49.58 kPa 719 count */
-       5628,   /*  49.64 kPa 720 count */
-       5620,   /*  49.69 kPa 721 count */
-       5612,   /*  49.75 kPa 722 count */
-       5604,   /*  49.80 kPa 723 count */
-       5596,   /*  49.85 kPa 724 count */
-       5588,   /*  49.91 kPa 725 count */
-       5580,   /*  49.96 kPa 726 count */
-       5572,   /*  50.02 kPa 727 count */
-       5564,   /*  50.07 kPa 728 count */
-       5556,   /*  50.13 kPa 729 count */
-       5548,   /*  50.18 kPa 730 count */
-       5540,   /*  50.23 kPa 731 count */
-       5532,   /*  50.29 kPa 732 count */
-       5524,   /*  50.34 kPa 733 count */
-       5516,   /*  50.40 kPa 734 count */
-       5508,   /*  50.45 kPa 735 count */
-       5500,   /*  50.51 kPa 736 count */
-       5492,   /*  50.56 kPa 737 count */
-       5484,   /*  50.61 kPa 738 count */
-       5476,   /*  50.67 kPa 739 count */
-       5468,   /*  50.72 kPa 740 count */
-       5461,   /*  50.78 kPa 741 count */
-       5453,   /*  50.83 kPa 742 count */
-       5445,   /*  50.89 kPa 743 count */
-       5437,   /*  50.94 kPa 744 count */
-       5429,   /*  50.99 kPa 745 count */
-       5421,   /*  51.05 kPa 746 count */
-       5413,   /*  51.10 kPa 747 count */
-       5405,   /*  51.16 kPa 748 count */
-       5398,   /*  51.21 kPa 749 count */
-       5390,   /*  51.27 kPa 750 count */
-       5382,   /*  51.32 kPa 751 count */
-       5374,   /*  51.37 kPa 752 count */
-       5366,   /*  51.43 kPa 753 count */
-       5358,   /*  51.48 kPa 754 count */
-       5351,   /*  51.54 kPa 755 count */
-       5343,   /*  51.59 kPa 756 count */
-       5335,   /*  51.65 kPa 757 count */
-       5327,   /*  51.70 kPa 758 count */
-       5319,   /*  51.75 kPa 759 count */
-       5312,   /*  51.81 kPa 760 count */
-       5304,   /*  51.86 kPa 761 count */
-       5296,   /*  51.92 kPa 762 count */
-       5288,   /*  51.97 kPa 763 count */
-       5281,   /*  52.03 kPa 764 count */
-       5273,   /*  52.08 kPa 765 count */
-       5265,   /*  52.13 kPa 766 count */
-       5257,   /*  52.19 kPa 767 count */
-       5250,   /*  52.24 kPa 768 count */
-       5242,   /*  52.30 kPa 769 count */
-       5234,   /*  52.35 kPa 770 count */
-       5226,   /*  52.41 kPa 771 count */
-       5219,   /*  52.46 kPa 772 count */
-       5211,   /*  52.51 kPa 773 count */
-       5203,   /*  52.57 kPa 774 count */
-       5196,   /*  52.62 kPa 775 count */
-       5188,   /*  52.68 kPa 776 count */
-       5180,   /*  52.73 kPa 777 count */
-       5173,   /*  52.79 kPa 778 count */
-       5165,   /*  52.84 kPa 779 count */
-       5157,   /*  52.89 kPa 780 count */
-       5150,   /*  52.95 kPa 781 count */
-       5142,   /*  53.00 kPa 782 count */
-       5134,   /*  53.06 kPa 783 count */
-       5127,   /*  53.11 kPa 784 count */
-       5119,   /*  53.17 kPa 785 count */
-       5112,   /*  53.22 kPa 786 count */
-       5104,   /*  53.27 kPa 787 count */
-       5096,   /*  53.33 kPa 788 count */
-       5089,   /*  53.38 kPa 789 count */
-       5081,   /*  53.44 kPa 790 count */
-       5074,   /*  53.49 kPa 791 count */
-       5066,   /*  53.55 kPa 792 count */
-       5058,   /*  53.60 kPa 793 count */
-       5051,   /*  53.65 kPa 794 count */
-       5043,   /*  53.71 kPa 795 count */
-       5036,   /*  53.76 kPa 796 count */
-       5028,   /*  53.82 kPa 797 count */
-       5021,   /*  53.87 kPa 798 count */
-       5013,   /*  53.93 kPa 799 count */
-       5006,   /*  53.98 kPa 800 count */
-       4998,   /*  54.03 kPa 801 count */
-       4991,   /*  54.09 kPa 802 count */
-       4983,   /*  54.14 kPa 803 count */
-       4976,   /*  54.20 kPa 804 count */
-       4968,   /*  54.25 kPa 805 count */
-       4961,   /*  54.31 kPa 806 count */
-       4953,   /*  54.36 kPa 807 count */
-       4946,   /*  54.41 kPa 808 count */
-       4938,   /*  54.47 kPa 809 count */
-       4931,   /*  54.52 kPa 810 count */
-       4923,   /*  54.58 kPa 811 count */
-       4916,   /*  54.63 kPa 812 count */
-       4908,   /*  54.69 kPa 813 count */
-       4901,   /*  54.74 kPa 814 count */
-       4893,   /*  54.79 kPa 815 count */
-       4886,   /*  54.85 kPa 816 count */
-       4879,   /*  54.90 kPa 817 count */
-       4871,   /*  54.96 kPa 818 count */
-       4864,   /*  55.01 kPa 819 count */
-       4856,   /*  55.07 kPa 820 count */
-       4849,   /*  55.12 kPa 821 count */
-       4842,   /*  55.17 kPa 822 count */
-       4834,   /*  55.23 kPa 823 count */
-       4827,   /*  55.28 kPa 824 count */
-       4819,   /*  55.34 kPa 825 count */
-       4812,   /*  55.39 kPa 826 count */
-       4805,   /*  55.45 kPa 827 count */
-       4797,   /*  55.50 kPa 828 count */
-       4790,   /*  55.55 kPa 829 count */
-       4783,   /*  55.61 kPa 830 count */
-       4775,   /*  55.66 kPa 831 count */
-       4768,   /*  55.72 kPa 832 count */
-       4761,   /*  55.77 kPa 833 count */
-       4753,   /*  55.83 kPa 834 count */
-       4746,   /*  55.88 kPa 835 count */
-       4739,   /*  55.93 kPa 836 count */
-       4731,   /*  55.99 kPa 837 count */
-       4724,   /*  56.04 kPa 838 count */
-       4717,   /*  56.10 kPa 839 count */
-       4709,   /*  56.15 kPa 840 count */
-       4702,   /*  56.21 kPa 841 count */
-       4695,   /*  56.26 kPa 842 count */
-       4688,   /*  56.31 kPa 843 count */
-       4680,   /*  56.37 kPa 844 count */
-       4673,   /*  56.42 kPa 845 count */
-       4666,   /*  56.48 kPa 846 count */
-       4659,   /*  56.53 kPa 847 count */
-       4651,   /*  56.58 kPa 848 count */
-       4644,   /*  56.64 kPa 849 count */
-       4637,   /*  56.69 kPa 850 count */
-       4630,   /*  56.75 kPa 851 count */
-       4622,   /*  56.80 kPa 852 count */
-       4615,   /*  56.86 kPa 853 count */
-       4608,   /*  56.91 kPa 854 count */
-       4601,   /*  56.96 kPa 855 count */
-       4594,   /*  57.02 kPa 856 count */
-       4586,   /*  57.07 kPa 857 count */
-       4579,   /*  57.13 kPa 858 count */
-       4572,   /*  57.18 kPa 859 count */
-       4565,   /*  57.24 kPa 860 count */
-       4558,   /*  57.29 kPa 861 count */
-       4550,   /*  57.34 kPa 862 count */
-       4543,   /*  57.40 kPa 863 count */
-       4536,   /*  57.45 kPa 864 count */
-       4529,   /*  57.51 kPa 865 count */
-       4522,   /*  57.56 kPa 866 count */
-       4515,   /*  57.62 kPa 867 count */
-       4508,   /*  57.67 kPa 868 count */
-       4500,   /*  57.72 kPa 869 count */
-       4493,   /*  57.78 kPa 870 count */
-       4486,   /*  57.83 kPa 871 count */
-       4479,   /*  57.89 kPa 872 count */
-       4472,   /*  57.94 kPa 873 count */
-       4465,   /*  58.00 kPa 874 count */
-       4458,   /*  58.05 kPa 875 count */
-       4451,   /*  58.10 kPa 876 count */
-       4444,   /*  58.16 kPa 877 count */
-       4437,   /*  58.21 kPa 878 count */
-       4429,   /*  58.27 kPa 879 count */
-       4422,   /*  58.32 kPa 880 count */
-       4415,   /*  58.38 kPa 881 count */
-       4408,   /*  58.43 kPa 882 count */
-       4401,   /*  58.48 kPa 883 count */
-       4394,   /*  58.54 kPa 884 count */
-       4387,   /*  58.59 kPa 885 count */
-       4380,   /*  58.65 kPa 886 count */
-       4373,   /*  58.70 kPa 887 count */
-       4366,   /*  58.76 kPa 888 count */
-       4359,   /*  58.81 kPa 889 count */
-       4352,   /*  58.86 kPa 890 count */
-       4345,   /*  58.92 kPa 891 count */
-       4338,   /*  58.97 kPa 892 count */
-       4331,   /*  59.03 kPa 893 count */
-       4324,   /*  59.08 kPa 894 count */
-       4317,   /*  59.14 kPa 895 count */
-       4310,   /*  59.19 kPa 896 count */
-       4303,   /*  59.24 kPa 897 count */
-       4296,   /*  59.30 kPa 898 count */
-       4289,   /*  59.35 kPa 899 count */
-       4282,   /*  59.41 kPa 900 count */
-       4275,   /*  59.46 kPa 901 count */
-       4268,   /*  59.52 kPa 902 count */
-       4261,   /*  59.57 kPa 903 count */
-       4254,   /*  59.62 kPa 904 count */
-       4247,   /*  59.68 kPa 905 count */
-       4240,   /*  59.73 kPa 906 count */
-       4234,   /*  59.79 kPa 907 count */
-       4227,   /*  59.84 kPa 908 count */
-       4220,   /*  59.90 kPa 909 count */
-       4213,   /*  59.95 kPa 910 count */
-       4206,   /*  60.00 kPa 911 count */
-       4199,   /*  60.06 kPa 912 count */
-       4192,   /*  60.11 kPa 913 count */
-       4185,   /*  60.17 kPa 914 count */
-       4178,   /*  60.22 kPa 915 count */
-       4171,   /*  60.28 kPa 916 count */
-       4164,   /*  60.33 kPa 917 count */
-       4158,   /*  60.38 kPa 918 count */
-       4151,   /*  60.44 kPa 919 count */
-       4144,   /*  60.49 kPa 920 count */
-       4137,   /*  60.55 kPa 921 count */
-       4130,   /*  60.60 kPa 922 count */
-       4123,   /*  60.66 kPa 923 count */
-       4116,   /*  60.71 kPa 924 count */
-       4110,   /*  60.76 kPa 925 count */
-       4103,   /*  60.82 kPa 926 count */
-       4096,   /*  60.87 kPa 927 count */
-       4089,   /*  60.93 kPa 928 count */
-       4082,   /*  60.98 kPa 929 count */
-       4076,   /*  61.04 kPa 930 count */
-       4069,   /*  61.09 kPa 931 count */
-       4062,   /*  61.14 kPa 932 count */
-       4055,   /*  61.20 kPa 933 count */
-       4048,   /*  61.25 kPa 934 count */
-       4042,   /*  61.31 kPa 935 count */
-       4035,   /*  61.36 kPa 936 count */
-       4028,   /*  61.42 kPa 937 count */
-       4021,   /*  61.47 kPa 938 count */
-       4014,   /*  61.52 kPa 939 count */
-       4008,   /*  61.58 kPa 940 count */
-       4001,   /*  61.63 kPa 941 count */
-       3994,   /*  61.69 kPa 942 count */
-       3987,   /*  61.74 kPa 943 count */
-       3981,   /*  61.80 kPa 944 count */
-       3974,   /*  61.85 kPa 945 count */
-       3967,   /*  61.90 kPa 946 count */
-       3960,   /*  61.96 kPa 947 count */
-       3954,   /*  62.01 kPa 948 count */
-       3947,   /*  62.07 kPa 949 count */
-       3940,   /*  62.12 kPa 950 count */
-       3934,   /*  62.18 kPa 951 count */
-       3927,   /*  62.23 kPa 952 count */
-       3920,   /*  62.28 kPa 953 count */
-       3913,   /*  62.34 kPa 954 count */
-       3907,   /*  62.39 kPa 955 count */
-       3900,   /*  62.45 kPa 956 count */
-       3893,   /*  62.50 kPa 957 count */
-       3887,   /*  62.56 kPa 958 count */
-       3880,   /*  62.61 kPa 959 count */
-       3873,   /*  62.66 kPa 960 count */
-       3867,   /*  62.72 kPa 961 count */
-       3860,   /*  62.77 kPa 962 count */
-       3853,   /*  62.83 kPa 963 count */
-       3847,   /*  62.88 kPa 964 count */
-       3840,   /*  62.94 kPa 965 count */
-       3833,   /*  62.99 kPa 966 count */
-       3827,   /*  63.04 kPa 967 count */
-       3820,   /*  63.10 kPa 968 count */
-       3814,   /*  63.15 kPa 969 count */
-       3807,   /*  63.21 kPa 970 count */
-       3800,   /*  63.26 kPa 971 count */
-       3794,   /*  63.32 kPa 972 count */
-       3787,   /*  63.37 kPa 973 count */
-       3780,   /*  63.42 kPa 974 count */
-       3774,   /*  63.48 kPa 975 count */
-       3767,   /*  63.53 kPa 976 count */
-       3761,   /*  63.59 kPa 977 count */
-       3754,   /*  63.64 kPa 978 count */
-       3748,   /*  63.70 kPa 979 count */
-       3741,   /*  63.75 kPa 980 count */
-       3734,   /*  63.80 kPa 981 count */
-       3728,   /*  63.86 kPa 982 count */
-       3721,   /*  63.91 kPa 983 count */
-       3715,   /*  63.97 kPa 984 count */
-       3708,   /*  64.02 kPa 985 count */
-       3702,   /*  64.08 kPa 986 count */
-       3695,   /*  64.13 kPa 987 count */
-       3688,   /*  64.18 kPa 988 count */
-       3682,   /*  64.24 kPa 989 count */
-       3675,   /*  64.29 kPa 990 count */
-       3669,   /*  64.35 kPa 991 count */
-       3662,   /*  64.40 kPa 992 count */
-       3656,   /*  64.46 kPa 993 count */
-       3649,   /*  64.51 kPa 994 count */
-       3643,   /*  64.56 kPa 995 count */
-       3636,   /*  64.62 kPa 996 count */
-       3630,   /*  64.67 kPa 997 count */
-       3623,   /*  64.73 kPa 998 count */
-       3617,   /*  64.78 kPa 999 count */
-       3610,   /*  64.84 kPa 1000 count */
-       3604,   /*  64.89 kPa 1001 count */
-       3597,   /*  64.94 kPa 1002 count */
-       3591,   /*  65.00 kPa 1003 count */
-       3584,   /*  65.05 kPa 1004 count */
-       3578,   /*  65.11 kPa 1005 count */
-       3571,   /*  65.16 kPa 1006 count */
-       3565,   /*  65.22 kPa 1007 count */
-       3559,   /*  65.27 kPa 1008 count */
-       3552,   /*  65.32 kPa 1009 count */
-       3546,   /*  65.38 kPa 1010 count */
-       3539,   /*  65.43 kPa 1011 count */
-       3533,   /*  65.49 kPa 1012 count */
-       3526,   /*  65.54 kPa 1013 count */
-       3520,   /*  65.60 kPa 1014 count */
-       3514,   /*  65.65 kPa 1015 count */
-       3507,   /*  65.70 kPa 1016 count */
-       3501,   /*  65.76 kPa 1017 count */
-       3494,   /*  65.81 kPa 1018 count */
-       3488,   /*  65.87 kPa 1019 count */
-       3481,   /*  65.92 kPa 1020 count */
-       3475,   /*  65.98 kPa 1021 count */
-       3469,   /*  66.03 kPa 1022 count */
-       3462,   /*  66.08 kPa 1023 count */
-       3456,   /*  66.14 kPa 1024 count */
-       3450,   /*  66.19 kPa 1025 count */
-       3443,   /*  66.25 kPa 1026 count */
-       3437,   /*  66.30 kPa 1027 count */
-       3430,   /*  66.36 kPa 1028 count */
-       3424,   /*  66.41 kPa 1029 count */
-       3418,   /*  66.46 kPa 1030 count */
-       3411,   /*  66.52 kPa 1031 count */
-       3405,   /*  66.57 kPa 1032 count */
-       3399,   /*  66.63 kPa 1033 count */
-       3392,   /*  66.68 kPa 1034 count */
-       3386,   /*  66.74 kPa 1035 count */
-       3380,   /*  66.79 kPa 1036 count */
-       3373,   /*  66.84 kPa 1037 count */
-       3367,   /*  66.90 kPa 1038 count */
-       3361,   /*  66.95 kPa 1039 count */
-       3354,   /*  67.01 kPa 1040 count */
-       3348,   /*  67.06 kPa 1041 count */
-       3342,   /*  67.12 kPa 1042 count */
-       3335,   /*  67.17 kPa 1043 count */
-       3329,   /*  67.22 kPa 1044 count */
-       3323,   /*  67.28 kPa 1045 count */
-       3316,   /*  67.33 kPa 1046 count */
-       3310,   /*  67.39 kPa 1047 count */
-       3304,   /*  67.44 kPa 1048 count */
-       3298,   /*  67.50 kPa 1049 count */
-       3291,   /*  67.55 kPa 1050 count */
-       3285,   /*  67.60 kPa 1051 count */
-       3279,   /*  67.66 kPa 1052 count */
-       3273,   /*  67.71 kPa 1053 count */
-       3266,   /*  67.77 kPa 1054 count */
-       3260,   /*  67.82 kPa 1055 count */
-       3254,   /*  67.88 kPa 1056 count */
-       3248,   /*  67.93 kPa 1057 count */
-       3241,   /*  67.98 kPa 1058 count */
-       3235,   /*  68.04 kPa 1059 count */
-       3229,   /*  68.09 kPa 1060 count */
-       3223,   /*  68.15 kPa 1061 count */
-       3216,   /*  68.20 kPa 1062 count */
-       3210,   /*  68.26 kPa 1063 count */
-       3204,   /*  68.31 kPa 1064 count */
-       3198,   /*  68.36 kPa 1065 count */
-       3191,   /*  68.42 kPa 1066 count */
-       3185,   /*  68.47 kPa 1067 count */
-       3179,   /*  68.53 kPa 1068 count */
-       3173,   /*  68.58 kPa 1069 count */
-       3167,   /*  68.64 kPa 1070 count */
-       3160,   /*  68.69 kPa 1071 count */
-       3154,   /*  68.74 kPa 1072 count */
-       3148,   /*  68.80 kPa 1073 count */
-       3142,   /*  68.85 kPa 1074 count */
-       3136,   /*  68.91 kPa 1075 count */
-       3130,   /*  68.96 kPa 1076 count */
-       3123,   /*  69.02 kPa 1077 count */
-       3117,   /*  69.07 kPa 1078 count */
-       3111,   /*  69.12 kPa 1079 count */
-       3105,   /*  69.18 kPa 1080 count */
-       3099,   /*  69.23 kPa 1081 count */
-       3093,   /*  69.29 kPa 1082 count */
-       3087,   /*  69.34 kPa 1083 count */
-       3080,   /*  69.40 kPa 1084 count */
-       3074,   /*  69.45 kPa 1085 count */
-       3068,   /*  69.50 kPa 1086 count */
-       3062,   /*  69.56 kPa 1087 count */
-       3056,   /*  69.61 kPa 1088 count */
-       3050,   /*  69.67 kPa 1089 count */
-       3044,   /*  69.72 kPa 1090 count */
-       3037,   /*  69.78 kPa 1091 count */
-       3031,   /*  69.83 kPa 1092 count */
-       3025,   /*  69.88 kPa 1093 count */
-       3019,   /*  69.94 kPa 1094 count */
-       3013,   /*  69.99 kPa 1095 count */
-       3007,   /*  70.05 kPa 1096 count */
-       3001,   /*  70.10 kPa 1097 count */
-       2995,   /*  70.15 kPa 1098 count */
-       2989,   /*  70.21 kPa 1099 count */
-       2983,   /*  70.26 kPa 1100 count */
-       2977,   /*  70.32 kPa 1101 count */
-       2970,   /*  70.37 kPa 1102 count */
-       2964,   /*  70.43 kPa 1103 count */
-       2958,   /*  70.48 kPa 1104 count */
-       2952,   /*  70.53 kPa 1105 count */
-       2946,   /*  70.59 kPa 1106 count */
-       2940,   /*  70.64 kPa 1107 count */
-       2934,   /*  70.70 kPa 1108 count */
-       2928,   /*  70.75 kPa 1109 count */
-       2922,   /*  70.81 kPa 1110 count */
-       2916,   /*  70.86 kPa 1111 count */
-       2910,   /*  70.91 kPa 1112 count */
-       2904,   /*  70.97 kPa 1113 count */
-       2898,   /*  71.02 kPa 1114 count */
-       2892,   /*  71.08 kPa 1115 count */
-       2886,   /*  71.13 kPa 1116 count */
-       2880,   /*  71.19 kPa 1117 count */
-       2874,   /*  71.24 kPa 1118 count */
-       2868,   /*  71.29 kPa 1119 count */
-       2862,   /*  71.35 kPa 1120 count */
-       2856,   /*  71.40 kPa 1121 count */
-       2850,   /*  71.46 kPa 1122 count */
-       2844,   /*  71.51 kPa 1123 count */
-       2838,   /*  71.57 kPa 1124 count */
-       2832,   /*  71.62 kPa 1125 count */
-       2826,   /*  71.67 kPa 1126 count */
-       2820,   /*  71.73 kPa 1127 count */
-       2814,   /*  71.78 kPa 1128 count */
-       2808,   /*  71.84 kPa 1129 count */
-       2802,   /*  71.89 kPa 1130 count */
-       2796,   /*  71.95 kPa 1131 count */
-       2790,   /*  72.00 kPa 1132 count */
-       2784,   /*  72.05 kPa 1133 count */
-       2778,   /*  72.11 kPa 1134 count */
-       2772,   /*  72.16 kPa 1135 count */
-       2766,   /*  72.22 kPa 1136 count */
-       2760,   /*  72.27 kPa 1137 count */
-       2754,   /*  72.33 kPa 1138 count */
-       2748,   /*  72.38 kPa 1139 count */
-       2743,   /*  72.43 kPa 1140 count */
-       2737,   /*  72.49 kPa 1141 count */
-       2731,   /*  72.54 kPa 1142 count */
-       2725,   /*  72.60 kPa 1143 count */
-       2719,   /*  72.65 kPa 1144 count */
-       2713,   /*  72.71 kPa 1145 count */
-       2707,   /*  72.76 kPa 1146 count */
-       2701,   /*  72.81 kPa 1147 count */
-       2695,   /*  72.87 kPa 1148 count */
-       2689,   /*  72.92 kPa 1149 count */
-       2683,   /*  72.98 kPa 1150 count */
-       2678,   /*  73.03 kPa 1151 count */
-       2672,   /*  73.09 kPa 1152 count */
-       2666,   /*  73.14 kPa 1153 count */
-       2660,   /*  73.19 kPa 1154 count */
-       2654,   /*  73.25 kPa 1155 count */
-       2648,   /*  73.30 kPa 1156 count */
-       2642,   /*  73.36 kPa 1157 count */
-       2636,   /*  73.41 kPa 1158 count */
-       2631,   /*  73.47 kPa 1159 count */
-       2625,   /*  73.52 kPa 1160 count */
-       2619,   /*  73.57 kPa 1161 count */
-       2613,   /*  73.63 kPa 1162 count */
-       2607,   /*  73.68 kPa 1163 count */
-       2601,   /*  73.74 kPa 1164 count */
-       2595,   /*  73.79 kPa 1165 count */
-       2590,   /*  73.85 kPa 1166 count */
-       2584,   /*  73.90 kPa 1167 count */
-       2578,   /*  73.95 kPa 1168 count */
-       2572,   /*  74.01 kPa 1169 count */
-       2566,   /*  74.06 kPa 1170 count */
-       2560,   /*  74.12 kPa 1171 count */
-       2555,   /*  74.17 kPa 1172 count */
-       2549,   /*  74.23 kPa 1173 count */
-       2543,   /*  74.28 kPa 1174 count */
-       2537,   /*  74.33 kPa 1175 count */
-       2531,   /*  74.39 kPa 1176 count */
-       2526,   /*  74.44 kPa 1177 count */
-       2520,   /*  74.50 kPa 1178 count */
-       2514,   /*  74.55 kPa 1179 count */
-       2508,   /*  74.61 kPa 1180 count */
-       2502,   /*  74.66 kPa 1181 count */
-       2497,   /*  74.71 kPa 1182 count */
-       2491,   /*  74.77 kPa 1183 count */
-       2485,   /*  74.82 kPa 1184 count */
-       2479,   /*  74.88 kPa 1185 count */
-       2473,   /*  74.93 kPa 1186 count */
-       2468,   /*  74.99 kPa 1187 count */
-       2462,   /*  75.04 kPa 1188 count */
-       2456,   /*  75.09 kPa 1189 count */
-       2450,   /*  75.15 kPa 1190 count */
-       2445,   /*  75.20 kPa 1191 count */
-       2439,   /*  75.26 kPa 1192 count */
-       2433,   /*  75.31 kPa 1193 count */
-       2427,   /*  75.37 kPa 1194 count */
-       2422,   /*  75.42 kPa 1195 count */
-       2416,   /*  75.47 kPa 1196 count */
-       2410,   /*  75.53 kPa 1197 count */
-       2405,   /*  75.58 kPa 1198 count */
-       2399,   /*  75.64 kPa 1199 count */
-       2393,   /*  75.69 kPa 1200 count */
-       2387,   /*  75.75 kPa 1201 count */
-       2382,   /*  75.80 kPa 1202 count */
-       2376,   /*  75.85 kPa 1203 count */
-       2370,   /*  75.91 kPa 1204 count */
-       2364,   /*  75.96 kPa 1205 count */
-       2359,   /*  76.02 kPa 1206 count */
-       2353,   /*  76.07 kPa 1207 count */
-       2347,   /*  76.13 kPa 1208 count */
-       2342,   /*  76.18 kPa 1209 count */
-       2336,   /*  76.23 kPa 1210 count */
-       2330,   /*  76.29 kPa 1211 count */
-       2325,   /*  76.34 kPa 1212 count */
-       2319,   /*  76.40 kPa 1213 count */
-       2313,   /*  76.45 kPa 1214 count */
-       2308,   /*  76.51 kPa 1215 count */
-       2302,   /*  76.56 kPa 1216 count */
-       2296,   /*  76.61 kPa 1217 count */
-       2291,   /*  76.67 kPa 1218 count */
-       2285,   /*  76.72 kPa 1219 count */
-       2279,   /*  76.78 kPa 1220 count */
-       2274,   /*  76.83 kPa 1221 count */
-       2268,   /*  76.89 kPa 1222 count */
-       2262,   /*  76.94 kPa 1223 count */
-       2257,   /*  76.99 kPa 1224 count */
-       2251,   /*  77.05 kPa 1225 count */
-       2245,   /*  77.10 kPa 1226 count */
-       2240,   /*  77.16 kPa 1227 count */
-       2234,   /*  77.21 kPa 1228 count */
-       2228,   /*  77.27 kPa 1229 count */
-       2223,   /*  77.32 kPa 1230 count */
-       2217,   /*  77.37 kPa 1231 count */
-       2212,   /*  77.43 kPa 1232 count */
-       2206,   /*  77.48 kPa 1233 count */
-       2200,   /*  77.54 kPa 1234 count */
-       2195,   /*  77.59 kPa 1235 count */
-       2189,   /*  77.65 kPa 1236 count */
-       2184,   /*  77.70 kPa 1237 count */
-       2178,   /*  77.75 kPa 1238 count */
-       2172,   /*  77.81 kPa 1239 count */
-       2167,   /*  77.86 kPa 1240 count */
-       2161,   /*  77.92 kPa 1241 count */
-       2156,   /*  77.97 kPa 1242 count */
-       2150,   /*  78.03 kPa 1243 count */
-       2144,   /*  78.08 kPa 1244 count */
-       2139,   /*  78.13 kPa 1245 count */
-       2133,   /*  78.19 kPa 1246 count */
-       2128,   /*  78.24 kPa 1247 count */
-       2122,   /*  78.30 kPa 1248 count */
-       2117,   /*  78.35 kPa 1249 count */
-       2111,   /*  78.41 kPa 1250 count */
-       2105,   /*  78.46 kPa 1251 count */
-       2100,   /*  78.51 kPa 1252 count */
-       2094,   /*  78.57 kPa 1253 count */
-       2089,   /*  78.62 kPa 1254 count */
-       2083,   /*  78.68 kPa 1255 count */
-       2078,   /*  78.73 kPa 1256 count */
-       2072,   /*  78.79 kPa 1257 count */
-       2067,   /*  78.84 kPa 1258 count */
-       2061,   /*  78.89 kPa 1259 count */
-       2056,   /*  78.95 kPa 1260 count */
-       2050,   /*  79.00 kPa 1261 count */
-       2045,   /*  79.06 kPa 1262 count */
-       2039,   /*  79.11 kPa 1263 count */
-       2033,   /*  79.17 kPa 1264 count */
-       2028,   /*  79.22 kPa 1265 count */
-       2022,   /*  79.27 kPa 1266 count */
-       2017,   /*  79.33 kPa 1267 count */
-       2011,   /*  79.38 kPa 1268 count */
-       2006,   /*  79.44 kPa 1269 count */
-       2000,   /*  79.49 kPa 1270 count */
-       1995,   /*  79.55 kPa 1271 count */
-       1989,   /*  79.60 kPa 1272 count */
-       1984,   /*  79.65 kPa 1273 count */
-       1978,   /*  79.71 kPa 1274 count */
-       1973,   /*  79.76 kPa 1275 count */
-       1967,   /*  79.82 kPa 1276 count */
-       1962,   /*  79.87 kPa 1277 count */
-       1957,   /*  79.93 kPa 1278 count */
-       1951,   /*  79.98 kPa 1279 count */
-       1946,   /*  80.03 kPa 1280 count */
-       1940,   /*  80.09 kPa 1281 count */
-       1935,   /*  80.14 kPa 1282 count */
-       1929,   /*  80.20 kPa 1283 count */
-       1924,   /*  80.25 kPa 1284 count */
-       1918,   /*  80.31 kPa 1285 count */
-       1913,   /*  80.36 kPa 1286 count */
-       1907,   /*  80.41 kPa 1287 count */
-       1902,   /*  80.47 kPa 1288 count */
-       1896,   /*  80.52 kPa 1289 count */
-       1891,   /*  80.58 kPa 1290 count */
-       1886,   /*  80.63 kPa 1291 count */
-       1880,   /*  80.69 kPa 1292 count */
-       1875,   /*  80.74 kPa 1293 count */
-       1869,   /*  80.79 kPa 1294 count */
-       1864,   /*  80.85 kPa 1295 count */
-       1858,   /*  80.90 kPa 1296 count */
-       1853,   /*  80.96 kPa 1297 count */
-       1848,   /*  81.01 kPa 1298 count */
-       1842,   /*  81.07 kPa 1299 count */
-       1837,   /*  81.12 kPa 1300 count */
-       1831,   /*  81.17 kPa 1301 count */
-       1826,   /*  81.23 kPa 1302 count */
-       1821,   /*  81.28 kPa 1303 count */
-       1815,   /*  81.34 kPa 1304 count */
-       1810,   /*  81.39 kPa 1305 count */
-       1804,   /*  81.45 kPa 1306 count */
-       1799,   /*  81.50 kPa 1307 count */
-       1794,   /*  81.55 kPa 1308 count */
-       1788,   /*  81.61 kPa 1309 count */
-       1783,   /*  81.66 kPa 1310 count */
-       1777,   /*  81.72 kPa 1311 count */
-       1772,   /*  81.77 kPa 1312 count */
-       1767,   /*  81.83 kPa 1313 count */
-       1761,   /*  81.88 kPa 1314 count */
-       1756,   /*  81.93 kPa 1315 count */
-       1751,   /*  81.99 kPa 1316 count */
-       1745,   /*  82.04 kPa 1317 count */
-       1740,   /*  82.10 kPa 1318 count */
-       1735,   /*  82.15 kPa 1319 count */
-       1729,   /*  82.21 kPa 1320 count */
-       1724,   /*  82.26 kPa 1321 count */
-       1718,   /*  82.31 kPa 1322 count */
-       1713,   /*  82.37 kPa 1323 count */
-       1708,   /*  82.42 kPa 1324 count */
-       1702,   /*  82.48 kPa 1325 count */
-       1697,   /*  82.53 kPa 1326 count */
-       1692,   /*  82.59 kPa 1327 count */
-       1686,   /*  82.64 kPa 1328 count */
-       1681,   /*  82.69 kPa 1329 count */
-       1676,   /*  82.75 kPa 1330 count */
-       1670,   /*  82.80 kPa 1331 count */
-       1665,   /*  82.86 kPa 1332 count */
-       1660,   /*  82.91 kPa 1333 count */
-       1655,   /*  82.97 kPa 1334 count */
-       1649,   /*  83.02 kPa 1335 count */
-       1644,   /*  83.07 kPa 1336 count */
-       1639,   /*  83.13 kPa 1337 count */
-       1633,   /*  83.18 kPa 1338 count */
-       1628,   /*  83.24 kPa 1339 count */
-       1623,   /*  83.29 kPa 1340 count */
-       1617,   /*  83.35 kPa 1341 count */
-       1612,   /*  83.40 kPa 1342 count */
-       1607,   /*  83.45 kPa 1343 count */
-       1602,   /*  83.51 kPa 1344 count */
-       1596,   /*  83.56 kPa 1345 count */
-       1591,   /*  83.62 kPa 1346 count */
-       1586,   /*  83.67 kPa 1347 count */
-       1580,   /*  83.72 kPa 1348 count */
-       1575,   /*  83.78 kPa 1349 count */
-       1570,   /*  83.83 kPa 1350 count */
-       1565,   /*  83.89 kPa 1351 count */
-       1559,   /*  83.94 kPa 1352 count */
-       1554,   /*  84.00 kPa 1353 count */
-       1549,   /*  84.05 kPa 1354 count */
-       1544,   /*  84.10 kPa 1355 count */
-       1538,   /*  84.16 kPa 1356 count */
-       1533,   /*  84.21 kPa 1357 count */
-       1528,   /*  84.27 kPa 1358 count */
-       1523,   /*  84.32 kPa 1359 count */
-       1517,   /*  84.38 kPa 1360 count */
-       1512,   /*  84.43 kPa 1361 count */
-       1507,   /*  84.48 kPa 1362 count */
-       1502,   /*  84.54 kPa 1363 count */
-       1496,   /*  84.59 kPa 1364 count */
-       1491,   /*  84.65 kPa 1365 count */
-       1486,   /*  84.70 kPa 1366 count */
-       1481,   /*  84.76 kPa 1367 count */
-       1475,   /*  84.81 kPa 1368 count */
-       1470,   /*  84.86 kPa 1369 count */
-       1465,   /*  84.92 kPa 1370 count */
-       1460,   /*  84.97 kPa 1371 count */
-       1455,   /*  85.03 kPa 1372 count */
-       1449,   /*  85.08 kPa 1373 count */
-       1444,   /*  85.14 kPa 1374 count */
-       1439,   /*  85.19 kPa 1375 count */
-       1434,   /*  85.24 kPa 1376 count */
-       1429,   /*  85.30 kPa 1377 count */
-       1423,   /*  85.35 kPa 1378 count */
-       1418,   /*  85.41 kPa 1379 count */
-       1413,   /*  85.46 kPa 1380 count */
-       1408,   /*  85.52 kPa 1381 count */
-       1403,   /*  85.57 kPa 1382 count */
-       1398,   /*  85.62 kPa 1383 count */
-       1392,   /*  85.68 kPa 1384 count */
-       1387,   /*  85.73 kPa 1385 count */
-       1382,   /*  85.79 kPa 1386 count */
-       1377,   /*  85.84 kPa 1387 count */
-       1372,   /*  85.90 kPa 1388 count */
-       1366,   /*  85.95 kPa 1389 count */
-       1361,   /*  86.00 kPa 1390 count */
-       1356,   /*  86.06 kPa 1391 count */
-       1351,   /*  86.11 kPa 1392 count */
-       1346,   /*  86.17 kPa 1393 count */
-       1341,   /*  86.22 kPa 1394 count */
-       1336,   /*  86.28 kPa 1395 count */
-       1330,   /*  86.33 kPa 1396 count */
-       1325,   /*  86.38 kPa 1397 count */
-       1320,   /*  86.44 kPa 1398 count */
-       1315,   /*  86.49 kPa 1399 count */
-       1310,   /*  86.55 kPa 1400 count */
-       1305,   /*  86.60 kPa 1401 count */
-       1300,   /*  86.66 kPa 1402 count */
-       1294,   /*  86.71 kPa 1403 count */
-       1289,   /*  86.76 kPa 1404 count */
-       1284,   /*  86.82 kPa 1405 count */
-       1279,   /*  86.87 kPa 1406 count */
-       1274,   /*  86.93 kPa 1407 count */
-       1269,   /*  86.98 kPa 1408 count */
-       1264,   /*  87.04 kPa 1409 count */
-       1259,   /*  87.09 kPa 1410 count */
-       1254,   /*  87.14 kPa 1411 count */
-       1248,   /*  87.20 kPa 1412 count */
-       1243,   /*  87.25 kPa 1413 count */
-       1238,   /*  87.31 kPa 1414 count */
-       1233,   /*  87.36 kPa 1415 count */
-       1228,   /*  87.42 kPa 1416 count */
-       1223,   /*  87.47 kPa 1417 count */
-       1218,   /*  87.52 kPa 1418 count */
-       1213,   /*  87.58 kPa 1419 count */
-       1208,   /*  87.63 kPa 1420 count */
-       1203,   /*  87.69 kPa 1421 count */
-       1198,   /*  87.74 kPa 1422 count */
-       1192,   /*  87.80 kPa 1423 count */
-       1187,   /*  87.85 kPa 1424 count */
-       1182,   /*  87.90 kPa 1425 count */
-       1177,   /*  87.96 kPa 1426 count */
-       1172,   /*  88.01 kPa 1427 count */
-       1167,   /*  88.07 kPa 1428 count */
-       1162,   /*  88.12 kPa 1429 count */
-       1157,   /*  88.18 kPa 1430 count */
-       1152,   /*  88.23 kPa 1431 count */
-       1147,   /*  88.28 kPa 1432 count */
-       1142,   /*  88.34 kPa 1433 count */
-       1137,   /*  88.39 kPa 1434 count */
-       1132,   /*  88.45 kPa 1435 count */
-       1127,   /*  88.50 kPa 1436 count */
-       1122,   /*  88.56 kPa 1437 count */
-       1117,   /*  88.61 kPa 1438 count */
-       1112,   /*  88.66 kPa 1439 count */
-       1107,   /*  88.72 kPa 1440 count */
-       1102,   /*  88.77 kPa 1441 count */
-       1097,   /*  88.83 kPa 1442 count */
-       1091,   /*  88.88 kPa 1443 count */
-       1086,   /*  88.94 kPa 1444 count */
-       1081,   /*  88.99 kPa 1445 count */
-       1076,   /*  89.04 kPa 1446 count */
-       1071,   /*  89.10 kPa 1447 count */
-       1066,   /*  89.15 kPa 1448 count */
-       1061,   /*  89.21 kPa 1449 count */
-       1056,   /*  89.26 kPa 1450 count */
-       1051,   /*  89.32 kPa 1451 count */
-       1046,   /*  89.37 kPa 1452 count */
-       1041,   /*  89.42 kPa 1453 count */
-       1036,   /*  89.48 kPa 1454 count */
-       1031,   /*  89.53 kPa 1455 count */
-       1026,   /*  89.59 kPa 1456 count */
-       1021,   /*  89.64 kPa 1457 count */
-       1016,   /*  89.70 kPa 1458 count */
-       1011,   /*  89.75 kPa 1459 count */
-       1006,   /*  89.80 kPa 1460 count */
-       1001,   /*  89.86 kPa 1461 count */
-       996,    /*  89.91 kPa 1462 count */
-       992,    /*  89.97 kPa 1463 count */
-       987,    /*  90.02 kPa 1464 count */
-       982,    /*  90.08 kPa 1465 count */
-       977,    /*  90.13 kPa 1466 count */
-       972,    /*  90.18 kPa 1467 count */
-       967,    /*  90.24 kPa 1468 count */
-       962,    /*  90.29 kPa 1469 count */
-       957,    /*  90.35 kPa 1470 count */
-       952,    /*  90.40 kPa 1471 count */
-       947,    /*  90.46 kPa 1472 count */
-       942,    /*  90.51 kPa 1473 count */
-       937,    /*  90.56 kPa 1474 count */
-       932,    /*  90.62 kPa 1475 count */
-       927,    /*  90.67 kPa 1476 count */
-       922,    /*  90.73 kPa 1477 count */
-       917,    /*  90.78 kPa 1478 count */
-       912,    /*  90.84 kPa 1479 count */
-       907,    /*  90.89 kPa 1480 count */
-       902,    /*  90.94 kPa 1481 count */
-       897,    /*  91.00 kPa 1482 count */
-       892,    /*  91.05 kPa 1483 count */
-       888,    /*  91.11 kPa 1484 count */
-       883,    /*  91.16 kPa 1485 count */
-       878,    /*  91.22 kPa 1486 count */
-       873,    /*  91.27 kPa 1487 count */
-       868,    /*  91.32 kPa 1488 count */
-       863,    /*  91.38 kPa 1489 count */
-       858,    /*  91.43 kPa 1490 count */
-       853,    /*  91.49 kPa 1491 count */
-       848,    /*  91.54 kPa 1492 count */
-       843,    /*  91.60 kPa 1493 count */
-       838,    /*  91.65 kPa 1494 count */
-       834,    /*  91.70 kPa 1495 count */
-       829,    /*  91.76 kPa 1496 count */
-       824,    /*  91.81 kPa 1497 count */
-       819,    /*  91.87 kPa 1498 count */
-       814,    /*  91.92 kPa 1499 count */
-       809,    /*  91.98 kPa 1500 count */
-       804,    /*  92.03 kPa 1501 count */
-       799,    /*  92.08 kPa 1502 count */
-       794,    /*  92.14 kPa 1503 count */
-       790,    /*  92.19 kPa 1504 count */
-       785,    /*  92.25 kPa 1505 count */
-       780,    /*  92.30 kPa 1506 count */
-       775,    /*  92.36 kPa 1507 count */
-       770,    /*  92.41 kPa 1508 count */
-       765,    /*  92.46 kPa 1509 count */
-       760,    /*  92.52 kPa 1510 count */
-       755,    /*  92.57 kPa 1511 count */
-       751,    /*  92.63 kPa 1512 count */
-       746,    /*  92.68 kPa 1513 count */
-       741,    /*  92.74 kPa 1514 count */
-       736,    /*  92.79 kPa 1515 count */
-       731,    /*  92.84 kPa 1516 count */
-       726,    /*  92.90 kPa 1517 count */
-       721,    /*  92.95 kPa 1518 count */
-       717,    /*  93.01 kPa 1519 count */
-       712,    /*  93.06 kPa 1520 count */
-       707,    /*  93.12 kPa 1521 count */
-       702,    /*  93.17 kPa 1522 count */
-       697,    /*  93.22 kPa 1523 count */
-       692,    /*  93.28 kPa 1524 count */
-       688,    /*  93.33 kPa 1525 count */
-       683,    /*  93.39 kPa 1526 count */
-       678,    /*  93.44 kPa 1527 count */
-       673,    /*  93.50 kPa 1528 count */
-       668,    /*  93.55 kPa 1529 count */
-       664,    /*  93.60 kPa 1530 count */
-       659,    /*  93.66 kPa 1531 count */
-       654,    /*  93.71 kPa 1532 count */
-       649,    /*  93.77 kPa 1533 count */
-       644,    /*  93.82 kPa 1534 count */
-       639,    /*  93.88 kPa 1535 count */
-       635,    /*  93.93 kPa 1536 count */
-       630,    /*  93.98 kPa 1537 count */
-       625,    /*  94.04 kPa 1538 count */
-       620,    /*  94.09 kPa 1539 count */
-       615,    /*  94.15 kPa 1540 count */
-       611,    /*  94.20 kPa 1541 count */
-       606,    /*  94.26 kPa 1542 count */
-       601,    /*  94.31 kPa 1543 count */
-       596,    /*  94.36 kPa 1544 count */
-       591,    /*  94.42 kPa 1545 count */
-       587,    /*  94.47 kPa 1546 count */
-       582,    /*  94.53 kPa 1547 count */
-       577,    /*  94.58 kPa 1548 count */
-       572,    /*  94.64 kPa 1549 count */
-       568,    /*  94.69 kPa 1550 count */
-       563,    /*  94.74 kPa 1551 count */
-       558,    /*  94.80 kPa 1552 count */
-       553,    /*  94.85 kPa 1553 count */
-       549,    /*  94.91 kPa 1554 count */
-       544,    /*  94.96 kPa 1555 count */
-       539,    /*  95.02 kPa 1556 count */
-       534,    /*  95.07 kPa 1557 count */
-       529,    /*  95.12 kPa 1558 count */
-       525,    /*  95.18 kPa 1559 count */
-       520,    /*  95.23 kPa 1560 count */
-       515,    /*  95.29 kPa 1561 count */
-       510,    /*  95.34 kPa 1562 count */
-       506,    /*  95.40 kPa 1563 count */
-       501,    /*  95.45 kPa 1564 count */
-       496,    /*  95.50 kPa 1565 count */
-       492,    /*  95.56 kPa 1566 count */
-       487,    /*  95.61 kPa 1567 count */
-       482,    /*  95.67 kPa 1568 count */
-       477,    /*  95.72 kPa 1569 count */
-       473,    /*  95.78 kPa 1570 count */
-       468,    /*  95.83 kPa 1571 count */
-       463,    /*  95.88 kPa 1572 count */
-       458,    /*  95.94 kPa 1573 count */
-       454,    /*  95.99 kPa 1574 count */
-       449,    /*  96.05 kPa 1575 count */
-       444,    /*  96.10 kPa 1576 count */
-       440,    /*  96.16 kPa 1577 count */
-       435,    /*  96.21 kPa 1578 count */
-       430,    /*  96.26 kPa 1579 count */
-       425,    /*  96.32 kPa 1580 count */
-       421,    /*  96.37 kPa 1581 count */
-       416,    /*  96.43 kPa 1582 count */
-       411,    /*  96.48 kPa 1583 count */
-       407,    /*  96.54 kPa 1584 count */
-       402,    /*  96.59 kPa 1585 count */
-       397,    /*  96.64 kPa 1586 count */
-       392,    /*  96.70 kPa 1587 count */
-       388,    /*  96.75 kPa 1588 count */
-       383,    /*  96.81 kPa 1589 count */
-       378,    /*  96.86 kPa 1590 count */
-       374,    /*  96.91 kPa 1591 count */
-       369,    /*  96.97 kPa 1592 count */
-       364,    /*  97.02 kPa 1593 count */
-       360,    /*  97.08 kPa 1594 count */
-       355,    /*  97.13 kPa 1595 count */
-       350,    /*  97.19 kPa 1596 count */
-       346,    /*  97.24 kPa 1597 count */
-       341,    /*  97.29 kPa 1598 count */
-       336,    /*  97.35 kPa 1599 count */
-       332,    /*  97.40 kPa 1600 count */
-       327,    /*  97.46 kPa 1601 count */
-       322,    /*  97.51 kPa 1602 count */
-       318,    /*  97.57 kPa 1603 count */
-       313,    /*  97.62 kPa 1604 count */
-       308,    /*  97.67 kPa 1605 count */
-       304,    /*  97.73 kPa 1606 count */
-       299,    /*  97.78 kPa 1607 count */
-       294,    /*  97.84 kPa 1608 count */
-       290,    /*  97.89 kPa 1609 count */
-       285,    /*  97.95 kPa 1610 count */
-       280,    /*  98.00 kPa 1611 count */
-       276,    /*  98.05 kPa 1612 count */
-       271,    /*  98.11 kPa 1613 count */
-       267,    /*  98.16 kPa 1614 count */
-       262,    /*  98.22 kPa 1615 count */
-       257,    /*  98.27 kPa 1616 count */
-       253,    /*  98.33 kPa 1617 count */
-       248,    /*  98.38 kPa 1618 count */
-       243,    /*  98.43 kPa 1619 count */
-       239,    /*  98.49 kPa 1620 count */
-       234,    /*  98.54 kPa 1621 count */
-       230,    /*  98.60 kPa 1622 count */
-       225,    /*  98.65 kPa 1623 count */
-       220,    /*  98.71 kPa 1624 count */
-       216,    /*  98.76 kPa 1625 count */
-       211,    /*  98.81 kPa 1626 count */
-       206,    /*  98.87 kPa 1627 count */
-       202,    /*  98.92 kPa 1628 count */
-       197,    /*  98.98 kPa 1629 count */
-       193,    /*  99.03 kPa 1630 count */
-       188,    /*  99.09 kPa 1631 count */
-       183,    /*  99.14 kPa 1632 count */
-       179,    /*  99.19 kPa 1633 count */
-       174,    /*  99.25 kPa 1634 count */
-       170,    /*  99.30 kPa 1635 count */
-       165,    /*  99.36 kPa 1636 count */
-       160,    /*  99.41 kPa 1637 count */
-       156,    /*  99.47 kPa 1638 count */
-       151,    /*  99.52 kPa 1639 count */
-       147,    /*  99.57 kPa 1640 count */
-       142,    /*  99.63 kPa 1641 count */
-       138,    /*  99.68 kPa 1642 count */
-       133,    /*  99.74 kPa 1643 count */
-       128,    /*  99.79 kPa 1644 count */
-       124,    /*  99.85 kPa 1645 count */
-       119,    /*  99.90 kPa 1646 count */
-       115,    /*  99.95 kPa 1647 count */
-       110,    /* 100.01 kPa 1648 count */
-       106,    /* 100.06 kPa 1649 count */
-       101,    /* 100.12 kPa 1650 count */
-       96,     /* 100.17 kPa 1651 count */
-       92,     /* 100.23 kPa 1652 count */
-       87,     /* 100.28 kPa 1653 count */
-       83,     /* 100.33 kPa 1654 count */
-       78,     /* 100.39 kPa 1655 count */
-       74,     /* 100.44 kPa 1656 count */
-       69,     /* 100.50 kPa 1657 count */
-       65,     /* 100.55 kPa 1658 count */
-       60,     /* 100.61 kPa 1659 count */
-       55,     /* 100.66 kPa 1660 count */
-       51,     /* 100.71 kPa 1661 count */
-       46,     /* 100.77 kPa 1662 count */
-       42,     /* 100.82 kPa 1663 count */
-       37,     /* 100.88 kPa 1664 count */
-       33,     /* 100.93 kPa 1665 count */
-       28,     /* 100.99 kPa 1666 count */
-       24,     /* 101.04 kPa 1667 count */
-       19,     /* 101.09 kPa 1668 count */
-       15,     /* 101.15 kPa 1669 count */
-       10,     /* 101.20 kPa 1670 count */
-       6,      /* 101.26 kPa 1671 count */
-       1,      /* 101.31 kPa 1672 count */
-       -3,     /* 101.37 kPa 1673 count */
-       -8,     /* 101.42 kPa 1674 count */
-       -12,    /* 101.47 kPa 1675 count */
-       -17,    /* 101.53 kPa 1676 count */
-       -21,    /* 101.58 kPa 1677 count */
-       -26,    /* 101.64 kPa 1678 count */
-       -30,    /* 101.69 kPa 1679 count */
-       -35,    /* 101.75 kPa 1680 count */
-       -39,    /* 101.80 kPa 1681 count */
-       -44,    /* 101.85 kPa 1682 count */
-       -48,    /* 101.91 kPa 1683 count */
-       -53,    /* 101.96 kPa 1684 count */
-       -57,    /* 102.02 kPa 1685 count */
-       -62,    /* 102.07 kPa 1686 count */
-       -66,    /* 102.13 kPa 1687 count */
-       -71,    /* 102.18 kPa 1688 count */
-       -75,    /* 102.23 kPa 1689 count */
-       -80,    /* 102.29 kPa 1690 count */
-       -84,    /* 102.34 kPa 1691 count */
-       -89,    /* 102.40 kPa 1692 count */
-       -93,    /* 102.45 kPa 1693 count */
-       -98,    /* 102.51 kPa 1694 count */
-       -102,   /* 102.56 kPa 1695 count */
-       -107,   /* 102.61 kPa 1696 count */
-       -111,   /* 102.67 kPa 1697 count */
-       -116,   /* 102.72 kPa 1698 count */
-       -120,   /* 102.78 kPa 1699 count */
-       -125,   /* 102.83 kPa 1700 count */
-       -129,   /* 102.89 kPa 1701 count */
-       -134,   /* 102.94 kPa 1702 count */
-       -138,   /* 102.99 kPa 1703 count */
-       -143,   /* 103.05 kPa 1704 count */
-       -147,   /* 103.10 kPa 1705 count */
-       -151,   /* 103.16 kPa 1706 count */
-       -156,   /* 103.21 kPa 1707 count */
-       -160,   /* 103.27 kPa 1708 count */
-       -165,   /* 103.32 kPa 1709 count */
-       -169,   /* 103.37 kPa 1710 count */
-       -174,   /* 103.43 kPa 1711 count */
-       -178,   /* 103.48 kPa 1712 count */
-       -183,   /* 103.54 kPa 1713 count */
-       -187,   /* 103.59 kPa 1714 count */
-       -191,   /* 103.65 kPa 1715 count */
-       -196,   /* 103.70 kPa 1716 count */
-       -200,   /* 103.75 kPa 1717 count */
-       -205,   /* 103.81 kPa 1718 count */
-       -209,   /* 103.86 kPa 1719 count */
-       -214,   /* 103.92 kPa 1720 count */
-       -218,   /* 103.97 kPa 1721 count */
-       -222,   /* 104.03 kPa 1722 count */
-       -227,   /* 104.08 kPa 1723 count */
-       -231,   /* 104.13 kPa 1724 count */
-       -236,   /* 104.19 kPa 1725 count */
-       -240,   /* 104.24 kPa 1726 count */
-       -245,   /* 104.30 kPa 1727 count */
-       -249,   /* 104.35 kPa 1728 count */
-       -253,   /* 104.41 kPa 1729 count */
-       -258,   /* 104.46 kPa 1730 count */
-       -262,   /* 104.51 kPa 1731 count */
-       -267,   /* 104.57 kPa 1732 count */
-       -271,   /* 104.62 kPa 1733 count */
-       -275,   /* 104.68 kPa 1734 count */
-       -280,   /* 104.73 kPa 1735 count */
-       -284,   /* 104.79 kPa 1736 count */
-       -289,   /* 104.84 kPa 1737 count */
-       -293,   /* 104.89 kPa 1738 count */
-       -297,   /* 104.95 kPa 1739 count */
-       -302,   /* 105.00 kPa 1740 count */
-       -306,   /* 105.06 kPa 1741 count */
-       -311,   /* 105.11 kPa 1742 count */
-       -315,   /* 105.17 kPa 1743 count */
-       -319,   /* 105.22 kPa 1744 count */
-       -324,   /* 105.27 kPa 1745 count */
-       -328,   /* 105.33 kPa 1746 count */
-       -332,   /* 105.38 kPa 1747 count */
-       -337,   /* 105.44 kPa 1748 count */
-       -341,   /* 105.49 kPa 1749 count */
-       -346,   /* 105.55 kPa 1750 count */
-       -350,   /* 105.60 kPa 1751 count */
-       -354,   /* 105.65 kPa 1752 count */
-       -359,   /* 105.71 kPa 1753 count */
-       -363,   /* 105.76 kPa 1754 count */
-       -367,   /* 105.82 kPa 1755 count */
-       -372,   /* 105.87 kPa 1756 count */
-       -376,   /* 105.93 kPa 1757 count */
-       -380,   /* 105.98 kPa 1758 count */
-       -385,   /* 106.03 kPa 1759 count */
-       -389,   /* 106.09 kPa 1760 count */
-       -394,   /* 106.14 kPa 1761 count */
-       -398,   /* 106.20 kPa 1762 count */
-       -402,   /* 106.25 kPa 1763 count */
-       -407,   /* 106.31 kPa 1764 count */
-       -411,   /* 106.36 kPa 1765 count */
-       -415,   /* 106.41 kPa 1766 count */
-       -420,   /* 106.47 kPa 1767 count */
-       -424,   /* 106.52 kPa 1768 count */
-       -428,   /* 106.58 kPa 1769 count */
-       -433,   /* 106.63 kPa 1770 count */
-       -437,   /* 106.69 kPa 1771 count */
-       -441,   /* 106.74 kPa 1772 count */
-       -446,   /* 106.79 kPa 1773 count */
-       -450,   /* 106.85 kPa 1774 count */
-       -454,   /* 106.90 kPa 1775 count */
-       -459,   /* 106.96 kPa 1776 count */
-       -463,   /* 107.01 kPa 1777 count */
-       -467,   /* 107.07 kPa 1778 count */
-       -472,   /* 107.12 kPa 1779 count */
-       -476,   /* 107.17 kPa 1780 count */
-       -480,   /* 107.23 kPa 1781 count */
-       -485,   /* 107.28 kPa 1782 count */
-       -489,   /* 107.34 kPa 1783 count */
-       -493,   /* 107.39 kPa 1784 count */
-       -497,   /* 107.45 kPa 1785 count */
-       -502,   /* 107.50 kPa 1786 count */
-       -506,   /* 107.55 kPa 1787 count */
-       -510,   /* 107.61 kPa 1788 count */
-       -515,   /* 107.66 kPa 1789 count */
-       -519,   /* 107.72 kPa 1790 count */
-       -523,   /* 107.77 kPa 1791 count */
-       -528,   /* 107.83 kPa 1792 count */
-       -532,   /* 107.88 kPa 1793 count */
-       -536,   /* 107.93 kPa 1794 count */
-       -540,   /* 107.99 kPa 1795 count */
-       -545,   /* 108.04 kPa 1796 count */
-       -549,   /* 108.10 kPa 1797 count */
-       -553,   /* 108.15 kPa 1798 count */
-       -558,   /* 108.21 kPa 1799 count */
-       -562,   /* 108.26 kPa 1800 count */
-       -566,   /* 108.31 kPa 1801 count */
-       -570,   /* 108.37 kPa 1802 count */
-       -575,   /* 108.42 kPa 1803 count */
-       -579,   /* 108.48 kPa 1804 count */
-       -583,   /* 108.53 kPa 1805 count */
-       -588,   /* 108.59 kPa 1806 count */
-       -592,   /* 108.64 kPa 1807 count */
-       -596,   /* 108.69 kPa 1808 count */
-       -600,   /* 108.75 kPa 1809 count */
-       -605,   /* 108.80 kPa 1810 count */
-       -609,   /* 108.86 kPa 1811 count */
-       -613,   /* 108.91 kPa 1812 count */
-       -617,   /* 108.97 kPa 1813 count */
-       -622,   /* 109.02 kPa 1814 count */
-       -626,   /* 109.07 kPa 1815 count */
-       -630,   /* 109.13 kPa 1816 count */
-       -634,   /* 109.18 kPa 1817 count */
-       -639,   /* 109.24 kPa 1818 count */
-       -643,   /* 109.29 kPa 1819 count */
-       -647,   /* 109.35 kPa 1820 count */
-       -651,   /* 109.40 kPa 1821 count */
-       -656,   /* 109.45 kPa 1822 count */
-       -660,   /* 109.51 kPa 1823 count */
-       -664,   /* 109.56 kPa 1824 count */
-       -668,   /* 109.62 kPa 1825 count */
-       -673,   /* 109.67 kPa 1826 count */
-       -677,   /* 109.73 kPa 1827 count */
-       -681,   /* 109.78 kPa 1828 count */
-       -685,   /* 109.83 kPa 1829 count */
-       -690,   /* 109.89 kPa 1830 count */
-       -694,   /* 109.94 kPa 1831 count */
-       -698,   /* 110.00 kPa 1832 count */
-       -702,   /* 110.05 kPa 1833 count */
-       -706,   /* 110.11 kPa 1834 count */
-       -711,   /* 110.16 kPa 1835 count */
-       -715,   /* 110.21 kPa 1836 count */
-       -719,   /* 110.27 kPa 1837 count */
-       -723,   /* 110.32 kPa 1838 count */
-       -728,   /* 110.38 kPa 1839 count */
-       -732,   /* 110.43 kPa 1840 count */
-       -736,   /* 110.48 kPa 1841 count */
-       -740,   /* 110.54 kPa 1842 count */
-       -744,   /* 110.59 kPa 1843 count */
-       -749,   /* 110.65 kPa 1844 count */
-       -753,   /* 110.70 kPa 1845 count */
-       -757,   /* 110.76 kPa 1846 count */
-       -761,   /* 110.81 kPa 1847 count */
-       -765,   /* 110.86 kPa 1848 count */
-       -770,   /* 110.92 kPa 1849 count */
-       -774,   /* 110.97 kPa 1850 count */
-       -778,   /* 111.03 kPa 1851 count */
-       -782,   /* 111.08 kPa 1852 count */
-       -786,   /* 111.14 kPa 1853 count */
-       -791,   /* 111.19 kPa 1854 count */
-       -795,   /* 111.24 kPa 1855 count */
-       -799,   /* 111.30 kPa 1856 count */
-       -803,   /* 111.35 kPa 1857 count */
-       -807,   /* 111.41 kPa 1858 count */
-       -812,   /* 111.46 kPa 1859 count */
-       -816,   /* 111.52 kPa 1860 count */
-       -820,   /* 111.57 kPa 1861 count */
-       -824,   /* 111.62 kPa 1862 count */
-       -828,   /* 111.68 kPa 1863 count */
-       -832,   /* 111.73 kPa 1864 count */
-       -837,   /* 111.79 kPa 1865 count */
-       -841,   /* 111.84 kPa 1866 count */
-       -845,   /* 111.90 kPa 1867 count */
-       -849,   /* 111.95 kPa 1868 count */
-       -853,   /* 112.00 kPa 1869 count */
-       -857,   /* 112.06 kPa 1870 count */
-       -862,   /* 112.11 kPa 1871 count */
-       -866,   /* 112.17 kPa 1872 count */
-       -870,   /* 112.22 kPa 1873 count */
-       -874,   /* 112.28 kPa 1874 count */
-       -878,   /* 112.33 kPa 1875 count */
-       -882,   /* 112.38 kPa 1876 count */
-       -887,   /* 112.44 kPa 1877 count */
-       -891,   /* 112.49 kPa 1878 count */
-       -895,   /* 112.55 kPa 1879 count */
-       -899,   /* 112.60 kPa 1880 count */
-       -903,   /* 112.66 kPa 1881 count */
-       -907,   /* 112.71 kPa 1882 count */
-       -911,   /* 112.76 kPa 1883 count */
-       -916,   /* 112.82 kPa 1884 count */
-       -920,   /* 112.87 kPa 1885 count */
-       -924,   /* 112.93 kPa 1886 count */
-       -928,   /* 112.98 kPa 1887 count */
-       -932,   /* 113.04 kPa 1888 count */
-       -936,   /* 113.09 kPa 1889 count */
-       -940,   /* 113.14 kPa 1890 count */
-       -945,   /* 113.20 kPa 1891 count */
-       -949,   /* 113.25 kPa 1892 count */
-       -953,   /* 113.31 kPa 1893 count */
-       -957,   /* 113.36 kPa 1894 count */
-       -961,   /* 113.42 kPa 1895 count */
-       -965,   /* 113.47 kPa 1896 count */
-       -969,   /* 113.52 kPa 1897 count */
-       -973,   /* 113.58 kPa 1898 count */
-       -978,   /* 113.63 kPa 1899 count */
-       -982,   /* 113.69 kPa 1900 count */
-       -986,   /* 113.74 kPa 1901 count */
-       -990,   /* 113.80 kPa 1902 count */
-       -994,   /* 113.85 kPa 1903 count */
-       -998,   /* 113.90 kPa 1904 count */
-       -1002,  /* 113.96 kPa 1905 count */
-       -1006,  /* 114.01 kPa 1906 count */
-       -1010,  /* 114.07 kPa 1907 count */
-       -1015,  /* 114.12 kPa 1908 count */
-       -1019,  /* 114.18 kPa 1909 count */
-       -1023,  /* 114.23 kPa 1910 count */
-       -1027,  /* 114.28 kPa 1911 count */
-       -1031,  /* 114.34 kPa 1912 count */
-       -1035,  /* 114.39 kPa 1913 count */
-       -1039,  /* 114.45 kPa 1914 count */
-       -1043,  /* 114.50 kPa 1915 count */
-       -1047,  /* 114.56 kPa 1916 count */
-       -1051,  /* 114.61 kPa 1917 count */
-       -1056,  /* 114.66 kPa 1918 count */
-       -1060,  /* 114.72 kPa 1919 count */
-       -1064,  /* 114.77 kPa 1920 count */
-       -1068,  /* 114.83 kPa 1921 count */
-       -1072,  /* 114.88 kPa 1922 count */
-       -1076,  /* 114.94 kPa 1923 count */
-       -1080,  /* 114.99 kPa 1924 count */
-       -1084,  /* 115.04 kPa 1925 count */
-       -1088,  /* 115.10 kPa 1926 count */
-       -1092,  /* 115.15 kPa 1927 count */
-       -1096,  /* 115.21 kPa 1928 count */
-       -1100,  /* 115.26 kPa 1929 count */
-       -1104,  /* 115.32 kPa 1930 count */
-       -1109,  /* 115.37 kPa 1931 count */
-       -1113,  /* 115.42 kPa 1932 count */
-       -1117,  /* 115.48 kPa 1933 count */
-       -1121,  /* 115.53 kPa 1934 count */
-       -1125,  /* 115.59 kPa 1935 count */
-       -1129,  /* 115.64 kPa 1936 count */
-       -1133,  /* 115.70 kPa 1937 count */
-       -1137,  /* 115.75 kPa 1938 count */
-       -1141,  /* 115.80 kPa 1939 count */
-       -1145,  /* 115.86 kPa 1940 count */
-       -1149,  /* 115.91 kPa 1941 count */
-       -1153,  /* 115.97 kPa 1942 count */
-       -1157,  /* 116.02 kPa 1943 count */
-       -1161,  /* 116.08 kPa 1944 count */
-       -1165,  /* 116.13 kPa 1945 count */
-       -1169,  /* 116.18 kPa 1946 count */
-       -1173,  /* 116.24 kPa 1947 count */
-       -1177,  /* 116.29 kPa 1948 count */
-       -1182,  /* 116.35 kPa 1949 count */
-       -1186,  /* 116.40 kPa 1950 count */
-       -1190,  /* 116.46 kPa 1951 count */
-       -1194,  /* 116.51 kPa 1952 count */
-       -1198,  /* 116.56 kPa 1953 count */
-       -1202,  /* 116.62 kPa 1954 count */
-       -1206,  /* 116.67 kPa 1955 count */
-       -1210,  /* 116.73 kPa 1956 count */
-       -1214,  /* 116.78 kPa 1957 count */
-       -1218,  /* 116.84 kPa 1958 count */
-       -1222,  /* 116.89 kPa 1959 count */
-       -1226,  /* 116.94 kPa 1960 count */
-       -1230,  /* 117.00 kPa 1961 count */
-       -1234,  /* 117.05 kPa 1962 count */
-       -1238,  /* 117.11 kPa 1963 count */
-       -1242,  /* 117.16 kPa 1964 count */
-       -1246,  /* 117.22 kPa 1965 count */
-       -1250,  /* 117.27 kPa 1966 count */
-       -1254,  /* 117.32 kPa 1967 count */
-       -1258,  /* 117.38 kPa 1968 count */
-       -1262,  /* 117.43 kPa 1969 count */
-       -1266,  /* 117.49 kPa 1970 count */
-       -1270,  /* 117.54 kPa 1971 count */
-       -1274,  /* 117.60 kPa 1972 count */
-       -1278,  /* 117.65 kPa 1973 count */
-       -1282,  /* 117.70 kPa 1974 count */
-       -1286,  /* 117.76 kPa 1975 count */
-       -1290,  /* 117.81 kPa 1976 count */
-       -1294,  /* 117.87 kPa 1977 count */
-       -1298,  /* 117.92 kPa 1978 count */
-       -1302,  /* 117.98 kPa 1979 count */
-       -1306,  /* 118.03 kPa 1980 count */
-       -1310,  /* 118.08 kPa 1981 count */
-       -1314,  /* 118.14 kPa 1982 count */
-       -1318,  /* 118.19 kPa 1983 count */
-       -1322,  /* 118.25 kPa 1984 count */
-       -1326,  /* 118.30 kPa 1985 count */
-       -1330,  /* 118.36 kPa 1986 count */
-       -1334,  /* 118.41 kPa 1987 count */
-       -1338,  /* 118.46 kPa 1988 count */
-       -1342,  /* 118.52 kPa 1989 count */
-       -1346,  /* 118.57 kPa 1990 count */
-       -1350,  /* 118.63 kPa 1991 count */
-       -1354,  /* 118.68 kPa 1992 count */
-       -1358,  /* 118.74 kPa 1993 count */
-       -1362,  /* 118.79 kPa 1994 count */
-       -1366,  /* 118.84 kPa 1995 count */
-       -1370,  /* 118.90 kPa 1996 count */
-       -1374,  /* 118.95 kPa 1997 count */
-       -1378,  /* 119.01 kPa 1998 count */
-       -1382,  /* 119.06 kPa 1999 count */
-       -1386,  /* 119.12 kPa 2000 count */
-       -1390,  /* 119.17 kPa 2001 count */
-       -1394,  /* 119.22 kPa 2002 count */
-       -1397,  /* 119.28 kPa 2003 count */
-       -1401,  /* 119.33 kPa 2004 count */
-       -1405,  /* 119.39 kPa 2005 count */
-       -1409,  /* 119.44 kPa 2006 count */
-       -1413,  /* 119.50 kPa 2007 count */
-       -1417,  /* 119.55 kPa 2008 count */
-       -1421,  /* 119.60 kPa 2009 count */
-       -1425,  /* 119.66 kPa 2010 count */
-       -1429,  /* 119.71 kPa 2011 count */
-       -1433,  /* 119.77 kPa 2012 count */
-       -1437,  /* 119.82 kPa 2013 count */
-       -1441,  /* 119.88 kPa 2014 count */
-       -1445,  /* 119.93 kPa 2015 count */
-       -1449,  /* 119.98 kPa 2016 count */
-       -1453,  /* 120.04 kPa 2017 count */
-       -1457,  /* 120.09 kPa 2018 count */
-       -1461,  /* 120.15 kPa 2019 count */
-       -1465,  /* 120.20 kPa 2020 count */
-       -1469,  /* 120.26 kPa 2021 count */
-       -1472,  /* 120.31 kPa 2022 count */
-       -1476,  /* 120.36 kPa 2023 count */
-       -1480,  /* 120.42 kPa 2024 count */
-       -1484,  /* 120.47 kPa 2025 count */
-       -1488,  /* 120.53 kPa 2026 count */
-       -1492,  /* 120.58 kPa 2027 count */
-       -1496,  /* 120.64 kPa 2028 count */
-       -1500,  /* 120.69 kPa 2029 count */
-       -1504,  /* 120.74 kPa 2030 count */
-       -1508,  /* 120.80 kPa 2031 count */
-       -1512,  /* 120.85 kPa 2032 count */
-       -1516,  /* 120.91 kPa 2033 count */
-       -1520,  /* 120.96 kPa 2034 count */
-       -1523,  /* 121.02 kPa 2035 count */
-       -1527,  /* 121.07 kPa 2036 count */
-       -1531,  /* 121.12 kPa 2037 count */
-       -1535,  /* 121.18 kPa 2038 count */
-       -1539,  /* 121.23 kPa 2039 count */
-       -1543,  /* 121.29 kPa 2040 count */
-       -1547,  /* 121.34 kPa 2041 count */
-       -1551,  /* 121.40 kPa 2042 count */
-       -1555,  /* 121.45 kPa 2043 count */
-       -1559,  /* 121.50 kPa 2044 count */
-       -1562,  /* 121.56 kPa 2045 count */
-       -1566,  /* 121.61 kPa 2046 count */
-       -1570,  /* 121.67 kPa 2047 count */
diff --git a/src/ao-make-product.5c b/src/ao-make-product.5c
deleted file mode 100644 (file)
index 933032d..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#!/bin/sh
-
-autoimport ParseArgs;
-
-void
-write_ucs2(string a, string description)
-{
-       int len = String::length(a);
-
-       printf("/* %s */\n", description);
-       printf("#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2);
-       printf("#define AO_%s_STRING \"%s\"\n", description, a);
-       printf("#define AO_%s_UCS2", description);
-       for (int i = 0; i < len; i++) {
-               int     c = a[i];
-               if (i > 0)
-                       printf(",");
-               if (0x20 <= c && c < 128)
-                       printf(" '%c', 0", c);
-               else
-                       printf(" LE_WORD(0x%04x),", c);
-       }
-       printf("\n\n");
-}
-
-void
-write_string(string a, string description)
-{
-       printf ("/* %s */\n", description);
-       printf ("#define AO_%s_STRING \"%s\"\n", description, a);
-}
-
-void
-write_int(int a, string description)
-{
-       printf ("/* %s */\n", description);
-       printf ("#define AO_%s_NUMBER %d\n\n", description, a);
-}
-
-string manufacturer = "altusmetrum.org";
-string product = "TeleMetrum";
-string version = "0.0";
-int serial = 1;
-int user_argind = 0;
-
-argdesc argd = {
-       .args = {
-               {
-                       .var = { .arg_string = &manufacturer },
-                       .abbr = 'm',
-                       .name = "manufacturer",
-                       .expr_name = "manf",
-                       .desc = "Manufacturer name." },
-               {
-                       .var = { .arg_string = &product },
-                       .abbr = 'p',
-                       .name = "product",
-                       .expr_name = "prod",
-                       .desc = "Product name." },
-               {
-                       .var = { .arg_int = &serial },
-                       .abbr = 's',
-                       .name = "serial",
-                       .expr_name = "number",
-                       .desc = "Serial number." },
-               {
-                       .var = { .arg_string = &version },
-                       .abbr = 'v',
-                       .name = "version",
-                       .expr_name = "string",
-                       .desc = "Program version." },
-       },
-       .prog_name = "usb descriptors",
-};
-
-void
-main()
-{
-       string[dim(argv)-1] nargv = {[n] = argv[n+1]};
-       parseargs(&argd, &nargv);
-       write_ucs2(manufacturer, "iManufacturer");
-       write_ucs2(product, "iProduct");
-       write_ucs2(sprintf("%06d", serial), "iSerial");
-       write_int(serial, "iSerial");
-       write_string(version, "iVersion");
-}
-
-main();
diff --git a/src/ao.h b/src/ao.h
deleted file mode 100644 (file)
index 85b7825..0000000
--- a/src/ao.h
+++ /dev/null
@@ -1,929 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef _AO_H_
-#define _AO_H_
-
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <stddef.h>
-#include "cc1111.h"
-
-#define TRUE 1
-#define FALSE 0
-
-/* Convert a __data pointer into an __xdata pointer */
-#define DATA_TO_XDATA(a)       ((void __xdata *) ((uint8_t) (a) | 0xff00))
-
-/* Stack runs from above the allocated __data space to 0xfe, which avoids
- * writing to 0xff as that triggers the stack overflow indicator
- */
-#define AO_STACK_START 0x80
-#define AO_STACK_END   0xfe
-#define AO_STACK_SIZE  (AO_STACK_END - AO_STACK_START + 1)
-
-/* An AltOS task */
-struct ao_task {
-       __xdata void *wchan;            /* current wait channel (NULL if running) */
-       uint8_t stack_count;            /* amount of saved stack */
-       uint8_t task_id;                /* index in the task array */
-       __code char *name;              /* task name */
-       uint8_t stack[AO_STACK_SIZE];   /* saved stack */
-};
-
-extern __xdata struct ao_task *__data ao_cur_task;
-
-#define AO_NUM_TASKS           16      /* maximum number of tasks */
-#define AO_NO_TASK             0       /* no task id */
-
-/*
- ao_task.c
- */
-
-/* Suspend the current task until wchan is awoken */
-void
-ao_sleep(__xdata void *wchan);
-
-/* Wake all tasks sleeping on wchan */
-void
-ao_wakeup(__xdata void *wchan);
-
-/* Yield the processor to another task */
-void
-ao_yield(void) _naked;
-
-/* Add a task to the run queue */
-void
-ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *name) __reentrant;
-
-/* Dump task info to console */
-void
-ao_task_info(void);
-
-/* Start the scheduler. This will not return */
-void
-ao_start_scheduler(void);
-
-/*
- * ao_panic.c
- */
-
-#define AO_PANIC_NO_TASK       1       /* AO_NUM_TASKS is not large enough */
-#define AO_PANIC_DMA           2       /* Attempt to start DMA while active */
-#define AO_PANIC_MUTEX         3       /* Mis-using mutex API */
-#define AO_PANIC_EE            4       /* Mis-using eeprom API */
-#define AO_PANIC_LOG           5       /* Failing to read/write log data */
-#define AO_PANIC_CMD           6       /* Too many command sets registered */
-
-/* Stop the operating system, beeping and blinking the reason */
-void
-ao_panic(uint8_t reason);
-
-/*
- * ao_timer.c
- */
-
-/* Our timer runs at 100Hz */
-#define AO_MS_TO_TICKS(ms)     ((ms) / 10)
-#define AO_SEC_TO_TICKS(s)     ((s) * 100)
-
-/* Returns the current time in ticks */
-uint16_t
-ao_time(void);
-
-/* Suspend the current task until ticks time has passed */
-void
-ao_delay(uint16_t ticks);
-
-/* Set the ADC interval */
-void
-ao_timer_set_adc_interval(uint8_t interval) __critical;
-
-/* Timer interrupt */
-void
-ao_timer_isr(void) interrupt 9;
-
-/* Initialize the timer */
-void
-ao_timer_init(void);
-
-/* Initialize the hardware clock. Must be called first */
-void
-ao_clock_init(void);
-
-/*
- * ao_adc.c
- */
-
-#define AO_ADC_RING    64
-#define ao_adc_ring_next(n)    (((n) + 1) & (AO_ADC_RING - 1))
-#define ao_adc_ring_prev(n)    (((n) - 1) & (AO_ADC_RING - 1))
-
-/*
- * One set of samples read from the A/D converter
- */
-struct ao_adc {
-       uint16_t        tick;           /* tick when the sample was read */
-       int16_t         accel;          /* accelerometer */
-       int16_t         pres;           /* pressure sensor */
-       int16_t         temp;           /* temperature sensor */
-       int16_t         v_batt;         /* battery voltage */
-       int16_t         sense_d;        /* drogue continuity sense */
-       int16_t         sense_m;        /* main continuity sense */
-};
-
-/*
- * A/D data is stored in a ring, with the next sample to be written
- * at ao_adc_head
- */
-extern volatile __xdata struct ao_adc  ao_adc_ring[AO_ADC_RING];
-extern volatile __data uint8_t         ao_adc_head;
-
-/* Trigger a conversion sequence (called from the timer interrupt) */
-void
-ao_adc_poll(void);
-
-/* Suspend the current task until another A/D sample is converted */
-void
-ao_adc_sleep(void);
-
-/* Get a copy of the last complete A/D sample set */
-void
-ao_adc_get(__xdata struct ao_adc *packet);
-
-/* The A/D interrupt handler */
-#if !AO_NO_ADC_ISR
-void
-ao_adc_isr(void) interrupt 1;
-#endif
-
-/* Initialize the A/D converter */
-void
-ao_adc_init(void);
-
-/*
- * ao_beep.c
- */
-
-/*
- * Various pre-defined beep frequencies
- *
- * frequency = 1/2 (24e6/32) / beep
- */
-
-#define AO_BEEP_LOW    150     /* 2500Hz */
-#define AO_BEEP_MID    94      /* 3989Hz */
-#define AO_BEEP_HIGH   75      /* 5000Hz */
-#define AO_BEEP_OFF    0       /* off */
-
-#define AO_BEEP_g      240     /* 1562.5Hz */
-#define AO_BEEP_gs     227     /* 1652Hz (1655Hz) */
-#define AO_BEEP_aa     214     /* 1752Hz (1754Hz) */
-#define AO_BEEP_bbf    202     /* 1856Hz (1858Hz) */
-#define AO_BEEP_bb     190     /* 1974Hz (1969Hz) */
-#define AO_BEEP_cc     180     /* 2083Hz (2086Hz) */
-#define AO_BEEP_ccs    170     /* 2205Hz (2210Hz) */
-#define AO_BEEP_dd     160     /* 2344Hz (2341Hz) */
-#define AO_BEEP_eef    151     /* 2483Hz (2480Hz) */
-#define AO_BEEP_ee     143     /* 2622Hz (2628Hz) */
-#define AO_BEEP_ff     135     /* 2778Hz (2784Hz) */
-#define AO_BEEP_ffs    127     /* 2953Hz (2950Hz) */
-#define AO_BEEP_gg     120     /* 3125Hz */
-#define AO_BEEP_ggs    113     /* 3319Hz (3311Hz) */
-#define AO_BEEP_aaa    107     /* 3504Hz (3508Hz) */
-#define AO_BEEP_bbbf   101     /* 3713Hz (3716Hz) */
-#define AO_BEEP_bbb    95      /* 3947Hz (3937Hz) */
-#define AO_BEEP_ccc    90      /* 4167Hz (4171Hz) */
-#define AO_BEEP_cccs   85      /* 4412Hz (4419Hz) */
-#define AO_BEEP_ddd    80      /* 4688Hz (4682Hz) */
-#define AO_BEEP_eeef   76      /* 4934Hz (4961Hz) */
-#define AO_BEEP_eee    71      /* 5282Hz (5256Hz) */
-#define AO_BEEP_fff    67      /* 5597Hz (5568Hz) */
-#define AO_BEEP_fffs   64      /* 5859Hz (5899Hz) */
-#define AO_BEEP_ggg    60      /* 6250Hz */
-
-/* Set the beeper to the specified tone */
-void
-ao_beep(uint8_t beep);
-
-/* Turn on the beeper for the specified time */
-void
-ao_beep_for(uint8_t beep, uint16_t ticks) __reentrant;
-
-/* Initialize the beeper */
-void
-ao_beep_init(void);
-
-/*
- * ao_led.c
- */
-
-#define AO_LED_NONE    0
-#define AO_LED_GREEN   1
-#define AO_LED_RED     2
-
-/* Turn on the specified LEDs */
-void
-ao_led_on(uint8_t colors);
-
-/* Turn off the specified LEDs */
-void
-ao_led_off(uint8_t colors);
-
-/* Set all of the LEDs to the specified state */
-void
-ao_led_set(uint8_t colors);
-
-/* Toggle the specified LEDs */
-void
-ao_led_toggle(uint8_t colors);
-
-/* Turn on the specified LEDs for the indicated interval */
-void
-ao_led_for(uint8_t colors, uint16_t ticks) __reentrant;
-
-/* Initialize the LEDs */
-void
-ao_led_init(uint8_t enable);
-
-/*
- * ao_usb.c
- */
-
-/* Put one character to the USB output queue */
-void
-ao_usb_putchar(char c);
-
-/* Get one character from the USB input queue */
-char
-ao_usb_getchar(void);
-
-/* Flush the USB output queue */
-void
-ao_usb_flush(void);
-
-/* USB interrupt handler */
-void
-ao_usb_isr(void) interrupt 6;
-
-/* Enable the USB controller */
-void
-ao_usb_enable(void);
-
-/* Disable the USB controller */
-void
-ao_usb_disable(void);
-
-/* Initialize the USB system */
-void
-ao_usb_init(void);
-
-/*
- * ao_cmd.c
- */
-
-enum ao_cmd_status {
-       ao_cmd_success = 0,
-       ao_cmd_lex_error = 1,
-       ao_cmd_syntax_error = 2,
-};
-
-extern __xdata uint16_t ao_cmd_lex_i;
-extern __xdata char    ao_cmd_lex_c;
-extern __xdata enum ao_cmd_status ao_cmd_status;
-
-void
-ao_cmd_lex(void);
-
-void
-ao_cmd_put8(uint8_t v);
-
-void
-ao_cmd_put16(uint16_t v);
-
-void
-ao_cmd_white(void);
-
-void
-ao_cmd_hex(void);
-
-void
-ao_cmd_decimal(void);
-
-struct ao_cmds {
-       char            cmd;
-       void            (*func)(void);
-       const char      *help;
-};
-
-void
-ao_cmd_register(__code struct ao_cmds *cmds);
-
-void
-ao_cmd_init(void);
-
-/*
- * ao_dma.c
- */
-
-/* Allocate a DMA channel. the 'done' parameter will be set to 1
- * when the dma is finished and will be used to wakeup any waiters
- */
-uint8_t
-ao_dma_alloc(__xdata uint8_t * done);
-
-/* Setup a DMA channel */
-void
-ao_dma_set_transfer(uint8_t id,
-                   void __xdata *srcaddr,
-                   void __xdata *dstaddr,
-                   uint16_t count,
-                   uint8_t cfg0,
-                   uint8_t cfg1);
-
-/* Start a DMA channel */
-void
-ao_dma_start(uint8_t id);
-
-/* Manually trigger a DMA channel */
-void
-ao_dma_trigger(uint8_t id);
-
-/* Abort a running DMA transfer */
-void
-ao_dma_abort(uint8_t id);
-
-/* DMA interrupt routine */
-void
-ao_dma_isr(void) interrupt 8;
-
-/*
- * ao_mutex.c
- */
-
-void
-ao_mutex_get(__xdata uint8_t *ao_mutex) __reentrant;
-
-void
-ao_mutex_put(__xdata uint8_t *ao_mutex) __reentrant;
-
-/*
- * ao_ee.c
- */
-
-/*
- * We reserve the last block on the device for
- * configuration space. Writes and reads in this
- * area return errors.
- */
-
-#define AO_EE_BLOCK_SIZE       ((uint16_t) (256))
-#define AO_EE_DEVICE_SIZE      ((uint32_t) 128 * (uint32_t) 1024)
-#define AO_EE_DATA_SIZE                (AO_EE_DEVICE_SIZE - (uint32_t) AO_EE_BLOCK_SIZE)
-#define AO_EE_CONFIG_BLOCK     ((uint16_t) (AO_EE_DATA_SIZE / AO_EE_BLOCK_SIZE))
-
-void
-ao_ee_flush(void) __reentrant;
-
-/* Write to the eeprom */
-uint8_t
-ao_ee_write(uint32_t pos, uint8_t *buf, uint16_t len) __reentrant;
-
-/* Read from the eeprom */
-uint8_t
-ao_ee_read(uint32_t pos, uint8_t *buf, uint16_t len) __reentrant;
-
-/* Write the config block (at the end of the eeprom) */
-uint8_t
-ao_ee_write_config(uint8_t *buf, uint16_t len) __reentrant;
-
-/* Read the config block (at the end of the eeprom) */
-uint8_t
-ao_ee_read_config(uint8_t *buf, uint16_t len) __reentrant;
-
-/* Initialize the EEPROM code */
-void
-ao_ee_init(void);
-
-/*
- * ao_log.c
- */
-
-/*
- * The data log is recorded in the eeprom as a sequence
- * of data packets.
- *
- * Each packet starts with a 4-byte header that has the
- * packet type, the packet checksum and the tick count. Then
- * they all contain 2 16 bit values which hold packet-specific
- * data.
- *
- * For each flight, the first packet
- * is FLIGHT packet, indicating the serial number of the
- * device and a unique number marking the number of flights
- * recorded by this device.
- *
- * During flight, data from the accelerometer and barometer
- * are recorded in SENSOR packets, using the raw 16-bit values
- * read from the A/D converter.
- *
- * Also during flight, but at a lower rate, the deployment
- * sensors are recorded in DEPLOY packets. The goal here is to
- * detect failure in the deployment circuits.
- *
- * STATE packets hold state transitions as the flight computer
- * transitions through different stages of the flight.
- */
-#define AO_LOG_FLIGHT          'F'
-#define AO_LOG_SENSOR          'A'
-#define AO_LOG_TEMP_VOLT       'T'
-#define AO_LOG_DEPLOY          'D'
-#define AO_LOG_STATE           'S'
-#define AO_LOG_GPS_TIME                'G'
-#define AO_LOG_GPS_LAT         'N'
-#define AO_LOG_GPS_LON         'W'
-#define AO_LOG_GPS_ALT         'H'
-
-#define AO_LOG_POS_NONE                (~0UL)
-
-struct ao_log_record {
-       char                    type;
-       uint8_t                 csum;
-       uint16_t                tick;
-       union {
-               struct {
-                       int16_t         ground_accel;
-                       uint16_t        flight;
-               } flight;
-               struct {
-                       int16_t         accel;
-                       int16_t         pres;
-               } sensor;
-               struct {
-                       int16_t         temp;
-                       int16_t         v_batt;
-               } temp_volt;
-               struct {
-                       int16_t         drogue;
-                       int16_t         main;
-               } deploy;
-               struct {
-                       uint16_t        state;
-                       uint16_t        reason;
-               } state;
-               struct {
-                       uint8_t         hour;
-                       uint8_t         minute;
-                       uint8_t         second;
-                       uint8_t         flags;
-               } gps_time;
-               int32_t         gps_latitude;
-               int32_t         gps_longitude;
-               struct {
-                       int16_t         altitude;
-                       uint16_t        unused;
-               } gps_altitude;
-               struct {
-                       uint16_t        d0;
-                       uint16_t        d1;
-               } anon;
-       } u;
-};
-
-/* Write a record to the eeprom log */
-void
-ao_log_data(struct ao_log_record *log);
-
-/* Flush the log */
-void
-ao_log_flush(void);
-
-/* Log dumping API:
- * ao_log_dump_first() - get first log record
- * ao_log_dump_next()  - get next log record
- */
-extern __xdata struct ao_log_record ao_log_dump;
-
-/* Retrieve first log record for the current flight */
-uint8_t
-ao_log_dump_first(void);
-
-/* return next log record for the current flight */
-uint8_t
-ao_log_dump_next(void);
-
-/* Logging thread main routine */
-void
-ao_log(void);
-
-/* Start logging to eeprom */
-void
-ao_log_start(void);
-
-/* Stop logging */
-void
-ao_log_stop(void);
-
-/* Initialize the logging system */
-void
-ao_log_init(void);
-
-/*
- * ao_flight.c
- */
-
-enum ao_flight_state {
-       ao_flight_startup = 0,
-       ao_flight_idle = 1,
-       ao_flight_pad = 2,
-       ao_flight_boost = 3,
-       ao_flight_fast = 4,
-       ao_flight_coast = 5,
-       ao_flight_drogue = 6,
-       ao_flight_main = 7,
-       ao_flight_landed = 8,
-       ao_flight_invalid = 9
-};
-
-extern __xdata struct ao_adc           ao_flight_data;
-extern __pdata enum ao_flight_state    ao_flight_state;
-extern __pdata uint16_t                        ao_flight_tick;
-extern __pdata int16_t                 ao_flight_accel;
-extern __pdata int16_t                 ao_flight_pres;
-extern __pdata int32_t                 ao_flight_vel;
-extern __pdata int16_t                 ao_ground_pres;
-extern __pdata int16_t                 ao_ground_accel;
-extern __pdata int16_t                 ao_min_pres;
-extern __pdata uint16_t                        ao_launch_time;
-
-/* Flight thread */
-void
-ao_flight(void);
-
-/* Initialize flight thread */
-void
-ao_flight_init(void);
-
-/*
- * ao_report.c
- */
-
-void
-ao_report_init(void);
-
-/*
- * ao_convert.c
- *
- * Given raw data, convert to SI units
- */
-
-/* pressure from the sensor to altitude in meters */
-int16_t
-ao_pres_to_altitude(int16_t pres) __reentrant;
-
-int16_t
-ao_altitude_to_pres(int16_t alt) __reentrant;
-
-int16_t
-ao_temp_to_dC(int16_t temp) __reentrant;
-
-/*
- * ao_dbg.c
- *
- * debug another telemetrum board
- */
-
-/* Send a byte to the dbg target */
-void
-ao_dbg_send_byte(uint8_t byte);
-
-/* Receive a byte from the dbg target */
-uint8_t
-ao_dbg_recv_byte(void);
-
-/* Start a bulk transfer to/from dbg target memory */
-void
-ao_dbg_start_transfer(uint16_t addr);
-
-/* End a bulk transfer to/from dbg target memory */
-void
-ao_dbg_end_transfer(void);
-
-/* Write a byte to dbg target memory */
-void
-ao_dbg_write_byte(uint8_t byte);
-
-/* Read a byte from dbg target memory */
-uint8_t
-ao_dbg_read_byte(void);
-
-/* Enable dbg mode, switching use of the pins */
-void
-ao_dbg_debug_mode(void);
-
-/* Reset the dbg target */
-void
-ao_dbg_reset(void);
-
-void
-ao_dbg_init(void);
-
-/*
- * ao_serial.c
- */
-
-#if !AO_NO_SERIAL_ISR
-void
-ao_serial_rx1_isr(void) interrupt 3;
-
-void
-ao_serial_tx1_isr(void) interrupt 14;
-#endif
-
-char
-ao_serial_getchar(void) __critical;
-
-void
-ao_serial_putchar(char c) __critical;
-
-#define AO_SERIAL_SPEED_4800   0
-#define AO_SERIAL_SPEED_57600  1
-
-void
-ao_serial_set_speed(uint8_t speed);
-
-void
-ao_serial_init(void);
-
-/*
- * ao_gps.c
- */
-
-#define AO_GPS_NUM_SAT_MASK    (0xf << 0)
-#define AO_GPS_NUM_SAT_SHIFT   (0)
-
-#define AO_GPS_VALID           (1 << 4)
-#define AO_GPS_RUNNING         (1 << 5)
-
-struct ao_gps_data {
-       uint8_t                 hour;
-       uint8_t                 minute;
-       uint8_t                 second;
-       uint8_t                 flags;
-       int32_t                 latitude;       /* degrees * 10⁷ */
-       int32_t                 longitude;      /* degrees * 10⁷ */
-       int16_t                 altitude;       /* m */
-       uint16_t                ground_speed;   /* cm/s */
-       uint8_t                 course;         /* degrees / 2 */
-       uint8_t                 hdop;           /* * 5 */
-       int16_t                 climb_rate;     /* cm/s */
-       uint16_t                h_error;        /* m */
-       uint16_t                v_error;        /* m */
-};
-
-extern __xdata uint8_t ao_gps_mutex;
-extern __xdata struct ao_gps_data ao_gps_data;
-
-void
-ao_gps(void);
-
-void
-ao_gps_print(__xdata struct ao_gps_data *gps_data);
-
-void
-ao_gps_init(void);
-
-/*
- * ao_gps_report.c
- */
-
-void
-ao_gps_report(void);
-
-void
-ao_gps_report_init(void);
-
-/*
- * ao_telemetry.c
- */
-
-#define AO_MAX_CALLSIGN                8
-
-struct ao_telemetry {
-       uint8_t                 addr;
-       uint8_t                 flight_state;
-       int16_t                 flight_accel;
-       int16_t                 ground_accel;
-       int32_t                 flight_vel;
-       int16_t                 flight_pres;
-       int16_t                 ground_pres;
-       struct ao_adc           adc;
-       struct ao_gps_data      gps;
-       char                    callsign[AO_MAX_CALLSIGN];
-};
-
-/* Set delay between telemetry reports (0 to disable) */
-
-#define AO_TELEMETRY_INTERVAL_PAD      AO_MS_TO_TICKS(1000)
-#define AO_TELEMETRY_INTERVAL_FLIGHT   AO_MS_TO_TICKS(50)
-#define AO_TELEMETRY_INTERVAL_RECOVER  AO_MS_TO_TICKS(1000)
-
-void
-ao_telemetry_set_interval(uint16_t interval);
-
-void
-ao_telemetry_init(void);
-
-/*
- * ao_radio.c
- */
-
-void
-ao_radio_send(__xdata struct ao_telemetry *telemetry) __reentrant;
-
-struct ao_radio_recv {
-       struct ao_telemetry     telemetry;
-       int8_t                  rssi;
-       uint8_t                 status;
-};
-
-void
-ao_radio_recv(__xdata struct ao_radio_recv *recv) __reentrant;
-
-void
-ao_radio_init(void);
-
-/*
- * ao_monitor.c
- */
-
-extern const char const * const ao_state_names[];
-
-void
-ao_monitor(void);
-
-void
-ao_set_monitor(uint8_t monitoring);
-
-void
-ao_monitor_init(uint8_t led, uint8_t monitoring) __reentrant;
-
-/*
- * ao_stdio.c
- */
-
-void
-flush(void);
-
-/*
- * ao_ignite.c
- */
-
-enum ao_igniter {
-       ao_igniter_drogue = 0,
-       ao_igniter_main = 1
-};
-
-void
-ao_ignite(enum ao_igniter igniter);
-
-enum ao_igniter_status {
-       ao_igniter_unknown,     /* unknown status (ambiguous voltage) */
-       ao_igniter_ready,       /* continuity detected */
-       ao_igniter_active,      /* igniter firing */
-       ao_igniter_open,        /* open circuit detected */
-};
-
-enum ao_igniter_status
-ao_igniter_status(enum ao_igniter igniter);
-
-void
-ao_igniter_init(void);
-
-/*
- * ao_config.c
- */
-
-#define AO_CONFIG_MAJOR        1
-#define AO_CONFIG_MINOR        0
-
-struct ao_config {
-       uint8_t         major;
-       uint8_t         minor;
-       uint16_t        main_deploy;
-       int16_t         accel_zero_g;
-       uint8_t         radio_channel;
-       char            callsign[AO_MAX_CALLSIGN + 1];
-};
-
-extern __xdata struct ao_config ao_config;
-
-void
-ao_config_get(void);
-
-void
-ao_config_init(void);
-
-/*
- * ao_rssi.c
- */
-
-void
-ao_rssi_set(int rssi_value);
-
-void
-ao_rssi_init(uint8_t rssi_led);
-
-/*
- * ao_product.c
- *
- * values which need to be defined for
- * each instance of a product
- */
-
-extern const uint8_t ao_usb_descriptors [];
-extern const uint16_t ao_serial_number;
-extern const char ao_version[];
-extern const char ao_manufacturer[];
-extern const char ao_product[];
-
-/*
- * Fifos
- */
-
-#define AO_FIFO_SIZE   32
-
-struct ao_fifo {
-       uint8_t insert;
-       uint8_t remove;
-       char    fifo[AO_FIFO_SIZE];
-};
-
-#define ao_fifo_insert(f,c) do { \
-       (f).fifo[(f).insert] = (c); \
-       (f).insert = ((f).insert + 1) & (AO_FIFO_SIZE-1); \
-} while(0)
-
-#define ao_fifo_remove(f,c) do {\
-       c = (f).fifo[(f).remove]; \
-       (f).remove = ((f).remove + 1) & (AO_FIFO_SIZE-1); \
-} while(0)
-
-#define ao_fifo_full(f)                ((((f).insert + 1) & (AO_FIFO_SIZE-1)) == (f).remove)
-#define ao_fifo_empty(f)       ((f).insert == (f).remove)
-
-/*
- * ao_packet.c
- *
- * Packet-based command interface
- */
-
-#define AO_PACKET_MAX  32
-#define AO_PACKET_WIN  256
-
-#define AO_PACKET_FIN  (1 << 0)
-#define AO_PACKET_SYN  (1 << 1)
-#define AO_PACKET_RST  (1 << 2)
-#define AO_PACKET_ACK  (1 << 3)
-
-struct ao_packet {
-       uint8_t         addr;
-       uint8_t         flags;
-       uint16_t        seq;
-       uint16_t        ack;
-       uint16_t        window;
-       uint8_t         len;
-       uint8_t         d[AO_PACKET_MAX];
-};
-
-uint8_t
-ao_packet_connect(uint8_t dest);
-
-uint8_t
-ao_packet_accept(void);
-
-int
-ao_packet_send(uint8_t *data, int len);
-
-int
-ao_packet_recv(uint8_t *data, int len);
-
-void
-ao_packet_init(void);
-
-#endif /* _AO_H_ */
diff --git a/src/ao_adc.c b/src/ao_adc.c
deleted file mode 100644 (file)
index 26209dc..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-volatile __xdata struct ao_adc ao_adc_ring[AO_ADC_RING];
-volatile __data uint8_t                ao_adc_head;
-
-void
-ao_adc_poll(void)
-{
-       ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 0;
-}
-
-void
-ao_adc_sleep(void)
-{
-       ao_sleep(&ao_adc_ring);
-}
-
-void
-ao_adc_get(__xdata struct ao_adc *packet)
-{
-       uint8_t i = ao_adc_ring_prev(ao_adc_head);
-       memcpy(packet, &ao_adc_ring[i], sizeof (struct ao_adc));
-}
-
-void
-ao_adc_isr(void) interrupt 1
-{
-       uint8_t sequence;
-       uint8_t __xdata *a;
-
-       sequence = (ADCCON2 & ADCCON2_SCH_MASK) >> ADCCON2_SCH_SHIFT;
-       a = (uint8_t __xdata *) (&ao_adc_ring[ao_adc_head].accel + sequence);
-       a[0] = ADCL;
-       a[1] = ADCH;
-       if (sequence < 5) {
-               /* start next channel conversion */
-               ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | (sequence + 1);
-       } else {
-               /* record this conversion series */
-               ao_adc_ring[ao_adc_head].tick = ao_time();
-               ao_adc_head = ao_adc_ring_next(ao_adc_head);
-               ao_wakeup(ao_adc_ring);
-       }
-}
-
-static void
-ao_adc_dump(void)
-{
-       __xdata struct ao_adc   packet;
-       ao_adc_get(&packet);
-       printf("tick: %5u accel: %4d pres: %4d temp: %4d batt: %4d drogue: %4d main: %4d\n",
-              packet.tick, packet.accel >> 4, packet.pres >> 4, packet.temp >> 4,
-              packet.v_batt >> 4, packet.sense_d >> 4, packet.sense_m >> 4);
-}
-
-__code struct ao_cmds ao_adc_cmds[] = {
-       { 'a',  ao_adc_dump,    "a                                  Display current ADC values" },
-       { 0,    ao_adc_dump, NULL },
-};
-
-void
-ao_adc_init(void)
-{
-       ADCCFG = ((1 << 0) |    /* acceleration */
-                 (1 << 1) |    /* pressure */
-                 (1 << 2) |    /* temperature */
-                 (1 << 3) |    /* battery voltage */
-                 (1 << 4) |    /* drogue sense */
-                 (1 << 5));    /* main sense */
-
-       /* enable interrupts */
-       ADCIF = 0;
-       IEN0 |= IEN0_ADCIE;
-       ao_cmd_register(&ao_adc_cmds[0]);
-}
diff --git a/src/ao_adc_fake.c b/src/ao_adc_fake.c
deleted file mode 100644 (file)
index 6ca88d4..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-volatile __xdata struct ao_adc ao_adc_ring[AO_ADC_RING];
-volatile __data uint8_t                ao_adc_head;
-
-/* Stub for systems which have no ADC */
-void
-ao_adc_poll(void)
-{
-}
diff --git a/src/ao_beep.c b/src/ao_beep.c
deleted file mode 100644 (file)
index 3642f4c..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-void
-ao_beep(uint8_t beep)
-{
-       if (beep == 0) {
-               P2_0 = 0;
-               P2SEL = (P2SEL & ~P2SEL_SELP2_0_MASK) | P2SEL_SELP2_0_GPIO;
-               T4CTL = 0;
-       } else {
-               P2SEL = (P2SEL & ~P2SEL_SELP2_0_MASK) | P2SEL_SELP2_0_PERIPHERAL;
-               T4CC0 = beep;
-               T4CTL = TxCTL_DIV_32 | TxCTL_MODE_MODULO | TxCTL_START;
-       }
-}
-
-void
-ao_beep_for(uint8_t beep, uint16_t ticks) __reentrant
-{
-       ao_beep(beep);
-       ao_delay(ticks);
-       ao_beep(0);
-}
-
-void
-ao_beep_init(void)
-{
-       /* Our beeper is on P2_0, which is hooked to timer 4 using
-        * configuration alternative 2
-        */
-       P2_0 = 0;
-       P2SEL = (P2SEL & ~P2SEL_SELP2_0_MASK) | P2SEL_SELP2_0_GPIO;
-       PERCFG = (PERCFG & ~PERCFG_T4CFG_ALT_MASK) | PERCFG_T4CFG_ALT_2;
-       T4CCTL0 = TxCCTLy_CMP_TOGGLE|TxCCTLy_CMP_MODE_ENABLE;
-}
diff --git a/src/ao_cmd.c b/src/ao_cmd.c
deleted file mode 100644 (file)
index 33619b2..0000000
+++ /dev/null
@@ -1,318 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-__xdata uint16_t ao_cmd_lex_i;
-__xdata char   ao_cmd_lex_c;
-__xdata enum ao_cmd_status ao_cmd_status;
-static __xdata uint8_t lex_echo;
-
-#define CMD_LEN        32
-
-static __xdata char    cmd_line[CMD_LEN];
-static __xdata uint8_t cmd_len;
-static __xdata uint8_t cmd_i;
-
-static void
-put_string(char *s)
-{
-       __xdata char    c;
-       while (c = *s++)
-               putchar(c);
-}
-
-static void
-readline(void)
-{
-       __xdata char c;
-       if (lex_echo)
-               put_string("> ");
-       cmd_len = 0;
-       for (;;) {
-               flush();
-               c = getchar();
-               /* backspace/delete */
-               if (c == '\010' || c == '\177') {
-                       if (cmd_len != 0) {
-                               if (lex_echo)
-                                       put_string("\010 \010");
-                               --cmd_len;
-                       }
-                       continue;
-               }
-
-               /* ^U */
-               if (c == '\025') {
-                       while (cmd_len != 0) {
-                               if (lex_echo)
-                                       put_string("\010 \010");
-                               --cmd_len;
-                       }
-                       continue;
-               }
-
-               /* map CR to NL */
-               if (c == '\r')
-                       c = '\n';
-
-               if (c == '\n') {
-                       if (lex_echo)
-                               putchar('\n');
-                       break;
-               }
-
-               if (cmd_len >= CMD_LEN - 2) {
-                       if (lex_echo)
-                               putchar('\007');
-                       continue;
-               }
-               cmd_line[cmd_len++] = c;
-               if (lex_echo)
-                       putchar(c);
-       }
-       cmd_line[cmd_len++] = '\n';
-       cmd_line[cmd_len++] = '\0';
-       cmd_i = 0;
-}
-
-void
-ao_cmd_lex(void)
-{
-       ao_cmd_lex_c = '\n';
-       if (cmd_i < cmd_len)
-               ao_cmd_lex_c = cmd_line[cmd_i++];
-}
-
-static void
-putnibble(uint8_t v)
-{
-       if (v < 10)
-               putchar(v + '0');
-       else
-               putchar(v + ('a' - 10));
-}
-
-void
-ao_cmd_put16(uint16_t v)
-{
-       int8_t i;
-       for (i = 3; i >= 0; i--)
-               putnibble((v >> (i << 2)) & 0xf);
-}
-
-void
-ao_cmd_put8(uint8_t v)
-{
-       putnibble((v >> 4) & 0xf);
-       putnibble(v & 0xf);
-}
-
-void
-ao_cmd_white(void)
-{
-       while (ao_cmd_lex_c == ' ' || ao_cmd_lex_c == '\t')
-               ao_cmd_lex();
-}
-
-void
-ao_cmd_hex(void)
-{
-       __xdata uint8_t r = ao_cmd_lex_error;
-
-       ao_cmd_lex_i = 0;
-       ao_cmd_white();
-       for(;;) {
-               if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9')
-                       ao_cmd_lex_i = (ao_cmd_lex_i << 4) | (ao_cmd_lex_c - '0');
-               else if ('a' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'f')
-                       ao_cmd_lex_i = (ao_cmd_lex_i << 4) | (ao_cmd_lex_c - 'a' + 10);
-               else if ('A' <= ao_cmd_lex_c && ao_cmd_lex_c <= 'F')
-                       ao_cmd_lex_i = (ao_cmd_lex_i << 4) | (ao_cmd_lex_c - 'A' + 10);
-               else
-                       break;
-               r = ao_cmd_success;
-               ao_cmd_lex();
-       }
-       if (r != ao_cmd_success)
-               ao_cmd_status = r;
-}
-
-void
-ao_cmd_decimal(void)
-{
-       __xdata uint8_t r = ao_cmd_lex_error;
-
-       ao_cmd_lex_i = 0;
-       ao_cmd_white();
-       for(;;) {
-               if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9')
-                       ao_cmd_lex_i = (ao_cmd_lex_i * 10) + (ao_cmd_lex_c - '0');
-               else
-                       break;
-               r = ao_cmd_success;
-               ao_cmd_lex();
-       }
-       if (r != ao_cmd_success)
-               ao_cmd_status = r;
-}
-
-static void
-eol(void)
-{
-       while (ao_cmd_lex_c != '\n')
-               ao_cmd_lex();
-}
-
-static void
-dump(void)
-{
-       __xdata uint16_t c;
-       __xdata uint8_t * __xdata start, * __xdata end;
-
-       ao_cmd_hex();
-       start = (uint8_t __xdata *) ao_cmd_lex_i;
-       ao_cmd_hex();
-       end = (uint8_t __xdata *) ao_cmd_lex_i;
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       c = 0;
-       while (start <= end) {
-               if ((c & 7) == 0) {
-                       if (c)
-                               putchar('\n');
-                       ao_cmd_put16((uint16_t) start);
-               }
-               putchar(' ');
-               ao_cmd_put8(*start);
-               ++c;
-               start++;
-       }
-       putchar('\n');
-}
-
-static void
-echo(void)
-{
-       ao_cmd_hex();
-       lex_echo = ao_cmd_lex_i != 0;
-}
-
-static void
-version(void)
-{
-       printf("manufacturer     %s\n", ao_manufacturer);
-       printf("product          %s\n", ao_product);
-       printf("serial-number    %u\n", ao_serial_number);
-       printf("software-version %s\n", ao_version);
-}
-
-static const char help_txt[] = "All numbers are in hex";
-
-#define NUM_CMDS       11
-
-static __code struct ao_cmds   *__xdata (ao_cmds[NUM_CMDS]);
-static __xdata uint8_t         ao_ncmds;
-
-static void
-help(void)
-{
-       __xdata uint8_t cmds;
-       __xdata uint8_t cmd;
-       __code struct ao_cmds * __xdata cs;
-       puts(help_txt);
-       for (cmds = 0; cmds < ao_ncmds; cmds++) {
-               cs = ao_cmds[cmds];
-               for (cmd = 0; cs[cmd].cmd != '\0'; cmd++)
-                       puts(cs[cmd].help);
-       }
-}
-
-static void
-report(void)
-{
-       switch(ao_cmd_status) {
-       case ao_cmd_lex_error:
-       case ao_cmd_syntax_error:
-               puts("Syntax error");
-               ao_cmd_status = 0;
-               break;
-       }
-}
-
-void
-ao_cmd_register(__code struct ao_cmds *cmds)
-{
-       if (ao_ncmds >= NUM_CMDS)
-               ao_panic(AO_PANIC_CMD);
-       ao_cmds[ao_ncmds++] = cmds;
-}
-
-void
-ao_cmd(void *parameters)
-{
-       __xdata char    c;
-       __xdata uint8_t cmd, cmds;
-       __code struct ao_cmds * __xdata cs;
-       void (*__xdata func)(void);
-       (void) parameters;
-
-       lex_echo = 1;
-       for (;;) {
-               readline();
-               ao_cmd_lex();
-               ao_cmd_white();
-               c = ao_cmd_lex_c;
-               ao_cmd_lex();
-               if (c == '\r' || c == '\n')
-                       continue;
-               func = (void (*)(void)) NULL;
-               for (cmds = 0; cmds < ao_ncmds; cmds++) {
-                       cs = ao_cmds[cmds];
-                       for (cmd = 0; cs[cmd].cmd != '\0'; cmd++)
-                               if (cs[cmd].cmd == c) {
-                                       func = cs[cmd].func;
-                                       break;
-                               }
-                       if (func)
-                               break;
-               }
-               if (func)
-                       (*func)();
-               else
-                       ao_cmd_status = ao_cmd_syntax_error;
-               report();
-       }
-}
-
-__xdata struct ao_task ao_cmd_task;
-
-__code struct ao_cmds  ao_base_cmds[] = {
-       { '?', help,            "?                                  Print this message" },
-       { 'T', ao_task_info,    "T                                  Show task states" },
-       { 'E', echo,            "E <0 off, 1 on>                    Set command echo mode" },
-       { 'd', dump,            "d <start> <end>                    Dump memory" },
-       { 'v', version,         "v                                  Show version" },
-       { 0,    help,   NULL },
-};
-
-void
-ao_cmd_init(void)
-{
-       ao_cmd_register(&ao_base_cmds[0]);
-       ao_add_task(&ao_cmd_task, ao_cmd, "cmd");
-}
diff --git a/src/ao_config.c b/src/ao_config.c
deleted file mode 100644 (file)
index 657c7a8..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-__xdata struct ao_config ao_config;
-__xdata uint8_t ao_config_loaded;
-__xdata uint8_t ao_config_dirty;
-__xdata uint8_t ao_config_mutex;
-
-#define AO_CONFIG_DEFAULT_MAIN_DEPLOY  250
-#define AO_CONFIG_DEFAULT_RADIO_CHANNEL        0
-#define AO_CONFIG_DEFAULT_CALLSIGN     "KD7SQG"
-#define AO_CONFIG_DEFAULT_ACCEL_ZERO_G 16000
-
-static void
-_ao_config_put(void)
-{
-       ao_ee_write_config((uint8_t *) &ao_config, sizeof (ao_config));
-}
-
-static void
-_ao_config_get(void)
-{
-       if (ao_config_loaded)
-               return;
-       ao_ee_read_config((uint8_t *) &ao_config, sizeof (ao_config));
-       if (ao_config.major != AO_CONFIG_MAJOR) {
-               ao_config.major = AO_CONFIG_MAJOR;
-               ao_config.minor = AO_CONFIG_MINOR;
-               ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
-               ao_config.radio_channel = AO_CONFIG_DEFAULT_RADIO_CHANNEL;
-               ao_config.accel_zero_g = AO_CONFIG_DEFAULT_ACCEL_ZERO_G;
-               memset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
-               memcpy(&ao_config.callsign, AO_CONFIG_DEFAULT_CALLSIGN,
-                      sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
-               ao_config_dirty = 1;
-       }
-       /* deal with minor version issues here, at 0 we haven't any */
-       ao_config_loaded = 1;
-}
-
-void
-ao_config_get(void)
-{
-       ao_mutex_get(&ao_config_mutex);
-       _ao_config_get();
-       ao_mutex_put(&ao_config_mutex);
-}
-
-void
-ao_config_callsign_show(void)
-{
-       printf ("Callsign: \"%s\"\n", ao_config.callsign);
-}
-
-void
-ao_config_callsign_set(void) __reentrant
-{
-       uint8_t c;
-       char callsign[AO_MAX_CALLSIGN + 1];
-
-       ao_cmd_white();
-       c = 0;
-       while (ao_cmd_lex_c != '\n') {
-               if (c < AO_MAX_CALLSIGN)
-                       callsign[c++] = ao_cmd_lex_c;
-               else
-                       ao_cmd_status = ao_cmd_lex_error;
-               ao_cmd_lex();
-       }
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       ao_mutex_get(&ao_config_mutex);
-       _ao_config_get();
-       while (c < AO_MAX_CALLSIGN + 1)
-               callsign[c++] = '\0';
-       memcpy(&ao_config.callsign, &callsign,
-              AO_MAX_CALLSIGN + 1);
-       ao_config_dirty = 1;
-       ao_mutex_put(&ao_config_mutex);
-       ao_config_callsign_show();
-}
-
-void
-ao_config_radio_channel_show(void) __reentrant
-{
-       uint32_t        freq = 434550L + ao_config.radio_channel * 100L;
-       uint16_t        mhz = freq / 1000L;
-       uint16_t        khz = freq % 1000L;
-
-       printf("Radio channel: %d (%d.%03dMHz)\n",
-              ao_config.radio_channel, mhz, khz);
-}
-
-void
-ao_config_radio_channel_set(void) __reentrant
-{
-       ao_cmd_decimal();
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       ao_mutex_get(&ao_config_mutex);
-       _ao_config_get();
-       ao_config.radio_channel = ao_cmd_lex_i;
-       ao_config_dirty = 1;
-       ao_mutex_put(&ao_config_mutex);
-       ao_config_radio_channel_show();
-}
-
-void
-ao_config_main_deploy_show(void) __reentrant
-{
-       printf("Main deploy set to %d meters (%d feet)\n",
-              ao_config.main_deploy,
-              (int16_t) ((int32_t) ao_config.main_deploy * 328 / 100));
-}
-
-void
-ao_config_main_deploy_set(void) __reentrant
-{
-       ao_cmd_decimal();
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       ao_mutex_get(&ao_config_mutex);
-       _ao_config_get();
-       ao_config.main_deploy = ao_cmd_lex_i;
-       ao_config_dirty = 1;
-       ao_mutex_put(&ao_config_mutex);
-       ao_config_main_deploy_show();
-}
-
-void
-ao_config_accel_zero_g_show(void) __reentrant
-{
-       printf("Accel zero g point set to %d\n",
-              ao_config.accel_zero_g);
-}
-
-#define ZERO_G_SAMPLES 1000
-
-static int16_t
-ao_config_accel_zero_g_auto(void) __reentrant
-{
-       uint16_t        i;
-       int32_t         accel_total;
-       uint8_t         cal_adc_ring;
-
-       puts("Calibrating accelerometer..."); flush();
-       i = ZERO_G_SAMPLES;
-       accel_total = 0;
-       cal_adc_ring = ao_adc_head;
-       while (i) {
-               ao_sleep(&ao_adc_ring);
-               while (i && cal_adc_ring != ao_adc_head) {
-                       accel_total += (int32_t) ao_adc_ring[cal_adc_ring].accel;
-                       cal_adc_ring = ao_adc_ring_next(cal_adc_ring);
-                       i--;
-               }
-       }
-       return (int16_t) (accel_total / ZERO_G_SAMPLES);
-}
-void
-ao_config_accel_zero_g_set(void) __reentrant
-{
-       ao_cmd_decimal();
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       if (ao_cmd_lex_i == 0)
-               ao_cmd_lex_i = ao_config_accel_zero_g_auto();
-       ao_mutex_get(&ao_config_mutex);
-       _ao_config_get();
-       ao_config.accel_zero_g = ao_cmd_lex_i;
-       ao_config_dirty = 1;
-       ao_mutex_put(&ao_config_mutex);
-       ao_config_accel_zero_g_show();
-}
-
-struct ao_config_var {
-       char            cmd;
-       void            (*set)(void) __reentrant;
-       void            (*show)(void) __reentrant;
-       const char      *help;
-};
-
-void
-ao_config_help(void) __reentrant;
-
-void
-ao_config_show(void) __reentrant;
-
-void
-ao_config_write(void) __reentrant;
-
-__code struct ao_config_var ao_config_vars[] = {
-       { 'm',  ao_config_main_deploy_set,      ao_config_main_deploy_show,
-               "m <meters>  Set height above launch for main deploy (in meters)" },
-       { 'a',  ao_config_accel_zero_g_set,     ao_config_accel_zero_g_show,
-               "a <value>   Set accelerometer zero g point (0 for auto)" },
-       { 'r',  ao_config_radio_channel_set,    ao_config_radio_channel_show,
-               "r <channel> Set radio channel (freq = 434.550 + channel * .1)" },
-       { 'c',  ao_config_callsign_set,         ao_config_callsign_show,
-               "c <call>    Set callsign broadcast in each packet (8 char max)" },
-       { 's',  ao_config_show,                 ao_config_show,
-               "s           Show current config values" },
-       { 'w',  ao_config_write,                ao_config_write,
-               "w           Write current values to eeprom" },
-       { '?',  ao_config_help,                 ao_config_help,
-               "?           Show available config variables" },
-       { 0,    ao_config_main_deploy_set,      ao_config_main_deploy_show,
-               NULL },
-};
-
-void
-ao_config_set(void)
-{
-       char    c;
-       uint8_t cmd;
-       void (*__xdata func)(void) __reentrant;
-
-       ao_cmd_white();
-       c = ao_cmd_lex_c;
-       ao_cmd_lex();
-       func = 0;
-       for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
-               if (ao_config_vars[cmd].cmd == c) {
-                       func = ao_config_vars[cmd].set;
-                       break;
-               }
-       if (func)
-               (*func)();
-       else
-               ao_cmd_status = ao_cmd_syntax_error;
-}
-
-void
-ao_config_help(void) __reentrant
-{
-       uint8_t cmd;
-       for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
-               puts (ao_config_vars[cmd].help);
-}
-
-void
-ao_config_show(void) __reentrant
-{
-       uint8_t cmd;
-       for (cmd = 0; ao_config_vars[cmd].cmd != '\0'; cmd++)
-               if (ao_config_vars[cmd].show != ao_config_vars[cmd].set)
-                       (*ao_config_vars[cmd].show)();
-}
-
-void
-ao_config_write(void) __reentrant
-{
-       ao_mutex_get(&ao_config_mutex);
-       if (ao_config_dirty) {
-               _ao_config_put();
-               ao_config_dirty = 0;
-               printf("Saved\n");
-       }
-       ao_mutex_put(&ao_config_mutex);
-}
-
-__code struct ao_cmds ao_config_cmds[] = {
-       { 'c',  ao_config_set,  "c <var> <value>                    Set config variable (? for help, s to show)" },
-       { '\0', ao_config_set, NULL },
-};
-
-void
-ao_config_init(void)
-{
-       ao_cmd_register(&ao_config_cmds[0]);
-}
diff --git a/src/ao_convert.c b/src/ao_convert.c
deleted file mode 100644 (file)
index 57ed737..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-static const int16_t altitude_table[2048] = {
-#include "altitude.h"
-};
-
-int16_t
-ao_pres_to_altitude(int16_t pres) __reentrant
-{
-       pres = pres >> 4;
-       if (pres < 0) pres = 0;
-       if (pres > 2047) pres = 2047;
-       return altitude_table[pres];
-}
-
-int16_t
-ao_altitude_to_pres(int16_t alt) __reentrant
-{
-       int16_t pres;
-
-       for (pres = 0; pres < 2047; pres++)
-               if (altitude_table[pres] <= alt)
-                       break;
-       return pres << 4;
-}
-
-static __xdata uint8_t ao_temp_mutex;
-
-int16_t
-ao_temp_to_dC(int16_t temp) __reentrant
-{
-       int16_t ret;
-
-       ao_mutex_get(&ao_temp_mutex);
-       ret = (int16_t) ((temp >> 4) * 3300L / 2047L) - 500;
-       ao_mutex_put(&ao_temp_mutex);
-       return ret;
-}
diff --git a/src/ao_dbg.c b/src/ao_dbg.c
deleted file mode 100644 (file)
index c8dc6dd..0000000
+++ /dev/null
@@ -1,367 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-#define DBG_CLOCK      (1 << 3)
-#define DBG_DATA       (1 << 4)
-#define DBG_RESET_N    (1 << 5)
-
-#define DBG_CLOCK_PIN  (P0_3)
-#define DBG_DATA_PIN   (P0_4)
-#define DBG_RESET_N_PIN        (P0_5)
-
-static void
-ao_dbg_send_bits(uint8_t msk, uint8_t val)
-{
-       P0 = (P0 & ~msk) | (val & msk);
-       _asm
-               nop
-               nop
-       _endasm;
-}
-
-void
-ao_dbg_send_byte(uint8_t byte)
-{
-       __xdata uint8_t b, d;
-
-       P0 |= DBG_DATA;
-       P0DIR |= DBG_DATA;
-       for (b = 0; b < 8; b++) {
-               d = 0;
-               if (byte & 0x80)
-                       d = DBG_DATA;
-               byte <<= 1;
-               ao_dbg_send_bits(DBG_CLOCK|DBG_DATA, DBG_CLOCK|d);
-               ao_dbg_send_bits(DBG_CLOCK|DBG_DATA,     0    |d);
-       }
-       P0DIR &= ~DBG_DATA;
-}
-
-uint8_t
-ao_dbg_recv_byte(void)
-{
-       __xdata uint8_t byte, b;
-
-       byte = 0;
-       for (b = 0; b < 8; b++) {
-               byte = byte << 1;
-               ao_dbg_send_bits(DBG_CLOCK, DBG_CLOCK);
-               if (DBG_DATA_PIN)
-                       byte |= 1;
-               ao_dbg_send_bits(DBG_CLOCK, 0);
-       }
-       return byte;
-}
-
-/* 8051 instructions
- */
-#define NOP                    0x00
-#define MOV_direct_data                0x75
-#define LJMP                   0x02
-#define MOV_Rn_data(n)         (0x78 | (n))
-#define DJNZ_Rn_rel(n)         (0xd8 | (n))
-#define MOV_A_direct           0xe5
-#define MOV_direct1_direct2    0x85
-#define MOV_direct_A           0xf5
-#define MOV_DPTR_data16                0x90
-#define MOV_A_data             0x74
-#define MOVX_atDPTR_A          0xf0
-#define MOVX_A_atDPTR          0xe0
-#define INC_DPTR               0xa3
-#define TRAP                   0xa5
-#define SJMP                   0x80
-#define JB                     0x20
-
-#define DEBUG_INSTR(l)         (0x54 | (l))
-
-#define SFR_PSW                        0xD0
-#define SFR_DPL0               0x82
-#define SFR_DPH0               0x83
-#define SFR_DPL1               0x84
-#define SFR_DPH1               0x85
-
-__xdata uint8_t        save_acc;
-__xdata uint8_t save_psw;
-__xdata uint8_t save_dpl0;
-__xdata uint8_t save_dph0;
-__xdata uint8_t save_dpl1;
-__xdata uint8_t save_dph1;
-
-static uint8_t
-ao_dbg_inst1(uint8_t a) __reentrant
-{
-       ao_dbg_send_byte(DEBUG_INSTR(1));
-       ao_dbg_send_byte(a);
-       return ao_dbg_recv_byte();
-}
-
-static uint8_t
-ao_dbg_inst2(uint8_t a, uint8_t b) __reentrant
-{
-       ao_dbg_send_byte(DEBUG_INSTR(2));
-       ao_dbg_send_byte(a);
-       ao_dbg_send_byte(b);
-       return ao_dbg_recv_byte();
-}
-
-static uint8_t
-ao_dbg_inst3(uint8_t a, uint8_t b, uint8_t c) __reentrant
-{
-       ao_dbg_send_byte(DEBUG_INSTR(3));
-       ao_dbg_send_byte(a);
-       ao_dbg_send_byte(b);
-       ao_dbg_send_byte(c);
-       return ao_dbg_recv_byte();
-}
-
-void
-ao_dbg_start_transfer(uint16_t addr)
-{
-       save_acc  = ao_dbg_inst1(NOP);
-       save_psw  = ao_dbg_inst2(MOV_A_direct, SFR_PSW);
-       save_dpl0 = ao_dbg_inst2(MOV_A_direct, SFR_DPL0);
-       save_dph0 = ao_dbg_inst2(MOV_A_direct, SFR_DPH0);
-       save_dpl1 = ao_dbg_inst2(MOV_A_direct, SFR_DPL1);
-       save_dph1 = ao_dbg_inst2(MOV_A_direct, SFR_DPH1);
-       ao_dbg_inst3(MOV_DPTR_data16, addr >> 8, addr);
-}
-
-void
-ao_dbg_end_transfer(void)
-{
-       ao_dbg_inst3(MOV_direct_data, SFR_DPL0, save_dpl0);
-       ao_dbg_inst3(MOV_direct_data, SFR_DPH0, save_dph0);
-       ao_dbg_inst3(MOV_direct_data, SFR_DPL1, save_dpl1);
-       ao_dbg_inst3(MOV_direct_data, SFR_DPH1, save_dph1);
-       ao_dbg_inst3(MOV_direct_data, SFR_PSW, save_psw);
-       ao_dbg_inst2(MOV_A_data, save_acc);
-}
-
-void
-ao_dbg_write_byte(uint8_t byte)
-{
-       ao_dbg_inst2(MOV_A_data, byte);
-       ao_dbg_inst1(MOVX_atDPTR_A);
-       ao_dbg_inst1(INC_DPTR);
-}
-
-uint8_t
-ao_dbg_read_byte(void)
-{
-       ao_dbg_inst1(MOVX_A_atDPTR);
-       return ao_dbg_inst1(INC_DPTR);
-}
-
-static void
-ao_dbg_set_pins(void)
-{
-       /* Disable peripheral use of P0 */
-       ADCCFG = 0;
-       P0SEL = 0;
-
-
-       /* make P0_4 tri-state */
-       P0INP = DBG_DATA;
-       P2INP &= ~(P2INP_PDUP0_PULL_DOWN);
-
-       /* Raise RESET_N and CLOCK */
-       P0 = DBG_RESET_N | DBG_CLOCK;
-
-       /* RESET_N and CLOCK are outputs now */
-       P0DIR = DBG_RESET_N | DBG_CLOCK;
-}
-
-static void
-ao_dbg_long_delay(void)
-{
-       uint8_t n;
-
-       for (n = 0; n < 20; n++)
-               _asm nop _endasm;
-}
-
-void
-ao_dbg_debug_mode(void)
-{
-       ao_dbg_set_pins();
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|DBG_RESET_N);
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N,     0    |DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N,     0    |DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N,     0    |DBG_DATA|DBG_RESET_N);
-       ao_dbg_long_delay();
-}
-
-void
-ao_dbg_reset(void)
-{
-       ao_dbg_set_pins();
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|DBG_RESET_N);
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
-       ao_dbg_long_delay();
-       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|DBG_RESET_N);
-       ao_dbg_long_delay();
-}
-
-static void
-debug_enable(void)
-{
-       ao_dbg_debug_mode();
-}
-
-static void
-debug_reset(void)
-{
-       ao_dbg_reset();
-}
-
-static void
-debug_put(void)
-{
-       for (;;) {
-               ao_cmd_white ();
-               if (ao_cmd_lex_c == '\n')
-                       break;
-               ao_cmd_hex();
-               if (ao_cmd_status != ao_cmd_success)
-                       break;
-               ao_dbg_send_byte(ao_cmd_lex_i);
-       }
-}
-
-static void
-debug_get(void)
-{
-       __xdata uint16_t count;
-       __xdata uint16_t i;
-       __xdata uint8_t byte;
-       ao_cmd_hex();
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       count = ao_cmd_lex_i;
-       if (count > 256) {
-               ao_cmd_status = ao_cmd_syntax_error;
-               return;
-       }
-       for (i = 0; i < count; i++) {
-               if (i && (i & 7) == 0)
-                       putchar('\n');
-               byte = ao_dbg_recv_byte();
-               ao_cmd_put8(byte);
-               putchar(' ');
-       }
-       putchar('\n');
-}
-
-static uint8_t
-getnibble(void)
-{
-       __xdata char    c;
-
-       c = getchar();
-       if ('0' <= c && c <= '9')
-               return c - '0';
-       if ('a' <= c && c <= 'f')
-               return c - ('a' - 10);
-       if ('A' <= c && c <= 'F')
-               return c - ('A' - 10);
-       ao_cmd_status = ao_cmd_lex_error;
-       return 0;
-}
-
-static void
-debug_input(void)
-{
-       __xdata uint16_t count;
-       __xdata uint16_t addr;
-       __xdata uint8_t b;
-       __xdata uint8_t i;
-
-       ao_cmd_hex();
-       count = ao_cmd_lex_i;
-       ao_cmd_hex();
-       addr = ao_cmd_lex_i;
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       ao_dbg_start_transfer(addr);
-       i = 0;
-       while (count--) {
-               if (!(i++ & 7))
-                       putchar('\n');
-               b = ao_dbg_read_byte();
-               ao_cmd_put8(b);
-       }
-       ao_dbg_end_transfer();
-       putchar('\n');
-}
-
-static void
-debug_output(void)
-{
-       __xdata uint16_t count;
-       __xdata uint16_t addr;
-       __xdata uint8_t b;
-
-       ao_cmd_hex();
-       count = ao_cmd_lex_i;
-       ao_cmd_hex();
-       addr = ao_cmd_lex_i;
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       ao_dbg_start_transfer(addr);
-       while (count--) {
-               b = getnibble() << 4;
-               b |= getnibble();
-               if (ao_cmd_status != ao_cmd_success)
-                       return;
-               ao_dbg_write_byte(b);
-       }
-       ao_dbg_end_transfer();
-}
-
-__code struct ao_cmds ao_dbg_cmds[7] = {
-       { 'D',  debug_enable,   "D                                  Enable debug mode" },
-       { 'G',  debug_get,      "G <count>                          Get data from debug port" },
-       { 'I',  debug_input,    "I <count> <addr>                   Input <count> bytes to target at <addr>" },
-       { 'O',  debug_output,   "O <count> <addr>                   Output <count> bytes to target at <addr>" },
-       { 'P',  debug_put,      "P <byte> ...                       Put data to debug port" },
-       { 'R',  debug_reset,    "R                                  Reset target" },
-       { 0, debug_reset,       0 },
-};
-
-void
-ao_dbg_init(void)
-{
-       ao_cmd_register(&ao_dbg_cmds[0]);
-}
diff --git a/src/ao_dma.c b/src/ao_dma.c
deleted file mode 100644 (file)
index a4d45f1..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-#define NUM_DMA        5
-
-/*
- * The config address for DMA0 is programmed
- * separately from that of DMA1-4, but for simplicity,
- * we make them all contiguous.
- */
-
-static __xdata struct cc_dma_channel ao_dma_config[NUM_DMA];
-static __xdata uint8_t * __xdata ao_dma_done[NUM_DMA];
-static __data uint8_t ao_next_dma;
-
-uint8_t
-ao_dma_alloc(__xdata uint8_t *done)
-{
-       uint8_t id;
-
-       if (ao_next_dma == NUM_DMA)
-               ao_panic(AO_PANIC_DMA);
-       id = ao_next_dma++;
-       ao_dma_done[id] = done;
-
-       /* When the first dma object is allocated, set up the DMA
-        * controller
-        */
-       if (id == 0) {
-               DMAIRQ = 0;
-               DMAIF = 0;
-               IEN1 |= IEN1_DMAIE;
-       }
-
-       return id;
-}
-
-void
-ao_dma_set_transfer(uint8_t id,
-                   void __xdata *srcaddr,
-                   void __xdata *dstaddr,
-                   uint16_t count,
-                   uint8_t cfg0,
-                   uint8_t cfg1)
-{
-       if (DMAARM & (1 << id))
-               ao_panic(AO_PANIC_DMA);
-       ao_dma_config[id].src_high = ((uint16_t) srcaddr) >> 8;
-       ao_dma_config[id].src_low = ((uint16_t) srcaddr);
-       ao_dma_config[id].dst_high = ((uint16_t) dstaddr) >> 8;
-       ao_dma_config[id].dst_low = ((uint16_t) dstaddr);
-       ao_dma_config[id].len_high = count >> 8;
-       ao_dma_config[id].len_low = count;
-       ao_dma_config[id].cfg0 = cfg0;
-       ao_dma_config[id].cfg1 = cfg1 | DMA_CFG1_IRQMASK;
-       if (id == 0) {
-               DMA0CFGH = ((uint16_t) (&ao_dma_config[0])) >> 8;
-               DMA0CFGL = ((uint16_t) (&ao_dma_config[0]));
-       } else {
-               DMA1CFGH = ((uint16_t) (&ao_dma_config[1])) >> 8;
-               DMA1CFGL = ((uint16_t) (&ao_dma_config[1]));
-       }
-}
-
-#define nop()  _asm nop _endasm;
-
-void
-ao_dma_start(uint8_t id)
-{
-       uint8_t mask = (1 << id);
-       DMAIRQ &= ~mask;
-       DMAARM = 0x80 | mask;
-       nop(); nop(); nop(); nop();
-       nop(); nop(); nop(); nop();
-       *(ao_dma_done[id]) = 0;
-       DMAARM = mask;
-       nop(); nop(); nop(); nop();
-       nop(); nop(); nop(); nop();
-       nop();
-}
-
-void
-ao_dma_trigger(uint8_t id)
-{
-       DMAREQ |= (1 << id);
-}
-
-void
-ao_dma_abort(uint8_t id)
-{
-       uint8_t mask = (1 << id);
-       DMAARM = 0x80 | mask;
-       DMAIRQ &= ~mask;
-}
-
-void
-ao_dma_isr(void) interrupt 8
-{
-       uint8_t id, mask;
-
-       /* Find the first DMA channel which is done */
-       mask = 1;
-       for (id = 0; id < ao_next_dma; id++) {
-               if (DMAIRQ & mask) {
-                       /* Clear CPU interrupt flag */
-                       DMAIF = 0;
-                       /* Clear the completed ID */
-                       DMAIRQ = ~mask;
-                       *(ao_dma_done[id]) = 1;
-                       ao_wakeup(ao_dma_done[id]);
-                       break;
-               }
-               mask <<= 1;
-       }
-}
diff --git a/src/ao_ee.c b/src/ao_ee.c
deleted file mode 100644 (file)
index f299b92..0000000
+++ /dev/null
@@ -1,459 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-#include "25lc1024.h"
-
-/*
- * Using SPI on USART 0, with P1_2 as the chip select
- */
-
-#define EE_CS          P1_2
-#define EE_CS_INDEX    2
-
-__xdata uint8_t ao_ee_dma_in_done;
-__xdata uint8_t ao_ee_dma_out_done;
-__xdata uint8_t ao_ee_mutex;
-
-uint8_t        ao_ee_dma_out_id;
-uint8_t ao_ee_dma_in_id;
-
-static __xdata uint8_t ao_ee_const = 0xff;
-
-#define ao_ee_delay() do { \
-       _asm nop _endasm; \
-       _asm nop _endasm; \
-       _asm nop _endasm; \
-} while(0)
-
-void ao_ee_cs_low(void)
-{
-       ao_ee_delay();
-       EE_CS = 0;
-       ao_ee_delay();
-}
-
-void ao_ee_cs_high(void)
-{
-       ao_ee_delay();
-       EE_CS = 1;
-       ao_ee_delay();
-}
-
-/* Send bytes over SPI.
- *
- * This sets up two DMA engines, one writing the data and another reading
- * bytes coming back.  We use the bytes coming back to tell when the transfer
- * is complete, as the transmit register is double buffered and hence signals
- * completion one byte before the transfer is actually complete
- */
-static void
-ao_ee_send(void __xdata *block, uint16_t len)
-{
-       ao_dma_set_transfer(ao_ee_dma_in_id,
-                           &U0DBUFXADDR,
-                           &ao_ee_const,
-                           len,
-                           DMA_CFG0_WORDSIZE_8 |
-                           DMA_CFG0_TMODE_SINGLE |
-                           DMA_CFG0_TRIGGER_URX0,
-                           DMA_CFG1_SRCINC_0 |
-                           DMA_CFG1_DESTINC_0 |
-                           DMA_CFG1_PRIORITY_NORMAL);
-
-       ao_dma_set_transfer(ao_ee_dma_out_id,
-                           block,
-                           &U0DBUFXADDR,
-                           len,
-                           DMA_CFG0_WORDSIZE_8 |
-                           DMA_CFG0_TMODE_SINGLE |
-                           DMA_CFG0_TRIGGER_UTX0,
-                           DMA_CFG1_SRCINC_1 |
-                           DMA_CFG1_DESTINC_0 |
-                           DMA_CFG1_PRIORITY_NORMAL);
-
-       ao_dma_start(ao_ee_dma_in_id);
-       ao_dma_start(ao_ee_dma_out_id);
-       ao_dma_trigger(ao_ee_dma_out_id);
-       __critical while (!ao_ee_dma_in_done)
-               ao_sleep(&ao_ee_dma_in_done);
-}
-
-/* Receive bytes over SPI.
- *
- * This sets up tow DMA engines, one reading the data and another
- * writing constant values to the SPI transmitter as that is what
- * clocks the data coming in.
- */
-static void
-ao_ee_recv(void __xdata *block, uint16_t len)
-{
-       ao_dma_set_transfer(ao_ee_dma_in_id,
-                           &U0DBUFXADDR,
-                           block,
-                           len,
-                           DMA_CFG0_WORDSIZE_8 |
-                           DMA_CFG0_TMODE_SINGLE |
-                           DMA_CFG0_TRIGGER_URX0,
-                           DMA_CFG1_SRCINC_0 |
-                           DMA_CFG1_DESTINC_1 |
-                           DMA_CFG1_PRIORITY_NORMAL);
-
-       ao_dma_set_transfer(ao_ee_dma_out_id,
-                           &ao_ee_const,
-                           &U0DBUFXADDR,
-                           len,
-                           DMA_CFG0_WORDSIZE_8 |
-                           DMA_CFG0_TMODE_SINGLE |
-                           DMA_CFG0_TRIGGER_UTX0,
-                           DMA_CFG1_SRCINC_0 |
-                           DMA_CFG1_DESTINC_0 |
-                           DMA_CFG1_PRIORITY_NORMAL);
-
-       ao_dma_start(ao_ee_dma_in_id);
-       ao_dma_start(ao_ee_dma_out_id);
-       ao_dma_trigger(ao_ee_dma_out_id);
-       __critical while (!ao_ee_dma_in_done)
-               ao_sleep(&ao_ee_dma_in_done);
-}
-
-#define EE_BLOCK       256
-
-struct ao_ee_instruction {
-       uint8_t instruction;
-       uint8_t address[3];
-} __xdata ao_ee_instruction;
-
-static void
-ao_ee_write_enable(void)
-{
-       ao_ee_cs_low();
-       ao_ee_instruction.instruction = EE_WREN;
-       ao_ee_send(&ao_ee_instruction, 1);
-       ao_ee_cs_high();
-}
-
-static uint8_t
-ao_ee_rdsr(void)
-{
-       ao_ee_cs_low();
-       ao_ee_instruction.instruction = EE_RDSR;
-       ao_ee_send(&ao_ee_instruction, 1);
-       ao_ee_recv(&ao_ee_instruction, 1);
-       ao_ee_cs_high();
-       return ao_ee_instruction.instruction;
-}
-
-static void
-ao_ee_wrsr(uint8_t status)
-{
-       ao_ee_cs_low();
-       ao_ee_instruction.instruction = EE_WRSR;
-       ao_ee_instruction.address[0] = status;
-       ao_ee_send(&ao_ee_instruction, 2);
-       ao_ee_cs_high();
-}
-
-#define EE_BLOCK_NONE  0xffff
-
-static __xdata uint8_t ao_ee_data[EE_BLOCK];
-static __pdata uint16_t ao_ee_block = EE_BLOCK_NONE;
-static __pdata uint8_t ao_ee_block_dirty;
-
-/* Write the current block to the EEPROM */
-static void
-ao_ee_write_block(void)
-{
-       uint8_t status;
-
-       status = ao_ee_rdsr();
-       if (status & (EE_STATUS_BP0|EE_STATUS_BP1|EE_STATUS_WPEN)) {
-               status &= ~(EE_STATUS_BP0|EE_STATUS_BP1|EE_STATUS_WPEN);
-               ao_ee_wrsr(status);
-       }
-       ao_ee_write_enable();
-       ao_ee_cs_low();
-       ao_ee_instruction.instruction = EE_WRITE;
-       ao_ee_instruction.address[0] = ao_ee_block >> 8;
-       ao_ee_instruction.address[1] = ao_ee_block;
-       ao_ee_instruction.address[2] = 0;
-       ao_ee_send(&ao_ee_instruction, 4);
-       ao_ee_send(ao_ee_data, EE_BLOCK);
-       ao_ee_cs_high();
-       for (;;) {
-               uint8_t status = ao_ee_rdsr();
-               if ((status & EE_STATUS_WIP) == 0)
-                       break;
-       }
-}
-
-/* Read the current block from the EEPROM */
-static void
-ao_ee_read_block(void)
-{
-       ao_ee_cs_low();
-       ao_ee_instruction.instruction = EE_READ;
-       ao_ee_instruction.address[0] = ao_ee_block >> 8;
-       ao_ee_instruction.address[1] = ao_ee_block;
-       ao_ee_instruction.address[2] = 0;
-       ao_ee_send(&ao_ee_instruction, 4);
-       ao_ee_recv(ao_ee_data, EE_BLOCK);
-       ao_ee_cs_high();
-}
-
-static void
-ao_ee_flush_internal(void)
-{
-       if (ao_ee_block_dirty) {
-               ao_ee_write_block();
-               ao_ee_block_dirty = 0;
-       }
-}
-
-static void
-ao_ee_fill(uint16_t block)
-{
-       if (block != ao_ee_block) {
-               ao_ee_flush_internal();
-               ao_ee_block = block;
-               ao_ee_read_block();
-       }
-}
-
-uint8_t
-ao_ee_write(uint32_t pos, uint8_t *buf, uint16_t len) __reentrant
-{
-       uint16_t block;
-       uint16_t this_len;
-       uint8_t this_off;
-
-       if (pos >= AO_EE_DATA_SIZE || pos + len > AO_EE_DATA_SIZE)
-               return 0;
-       while (len) {
-
-               /* Compute portion of transfer within
-                * a single block
-                */
-               this_off = pos;
-               this_len = 256 - (uint16_t) this_off;
-               block = (uint16_t) (pos >> 8);
-               if (this_len > len)
-                       this_len = len;
-               if (this_len & 0xff00)
-                       ao_panic(AO_PANIC_EE);
-
-               /* Transfer the data */
-               ao_mutex_get(&ao_ee_mutex); {
-                       if (this_len != 256)
-                               ao_ee_fill(block);
-                       else {
-                               ao_ee_flush_internal();
-                               ao_ee_block = block;
-                       }
-                       memcpy(ao_ee_data + this_off, buf, this_len);
-                       ao_ee_block_dirty = 1;
-               } ao_mutex_put(&ao_ee_mutex);
-
-               /* See how much is left */
-               buf += this_len;
-               len -= this_len;
-       }
-       return 1;
-}
-
-uint8_t
-ao_ee_read(uint32_t pos, uint8_t *buf, uint16_t len) __reentrant
-{
-       uint16_t block;
-       uint16_t this_len;
-       uint8_t this_off;
-
-       if (pos >= AO_EE_DATA_SIZE || pos + len > AO_EE_DATA_SIZE)
-               return 0;
-       while (len) {
-
-               /* Compute portion of transfer within
-                * a single block
-                */
-               this_off = pos;
-               this_len = 256 - (uint16_t) this_off;
-               block = (uint16_t) (pos >> 8);
-               if (this_len > len)
-                       this_len = len;
-               if (this_len & 0xff00)
-                       ao_panic(AO_PANIC_EE);
-
-               /* Transfer the data */
-               ao_mutex_get(&ao_ee_mutex); {
-                       ao_ee_fill(block);
-                       memcpy(buf, ao_ee_data + this_off, this_len);
-               } ao_mutex_put(&ao_ee_mutex);
-
-               /* See how much is left */
-               buf += this_len;
-               len -= this_len;
-       }
-       return 1;
-}
-
-void
-ao_ee_flush(void) __reentrant
-{
-       ao_mutex_get(&ao_ee_mutex); {
-               ao_ee_flush_internal();
-       } ao_mutex_put(&ao_ee_mutex);
-}
-
-/*
- * Read/write the config block, which is in
- * the last block of the ao_eeprom
- */
-uint8_t
-ao_ee_write_config(uint8_t *buf, uint16_t len) __reentrant
-{
-       if (len > AO_EE_BLOCK_SIZE)
-               return 0;
-       ao_mutex_get(&ao_ee_mutex); {
-               ao_ee_fill(AO_EE_CONFIG_BLOCK);
-               memcpy(ao_ee_data, buf, len);
-               ao_ee_block_dirty = 1;
-               ao_ee_flush_internal();
-       } ao_mutex_put(&ao_ee_mutex);
-       return 1;
-}
-
-uint8_t
-ao_ee_read_config(uint8_t *buf, uint16_t len) __reentrant
-{
-       if (len > AO_EE_BLOCK_SIZE)
-               return 0;
-       ao_mutex_get(&ao_ee_mutex); {
-               ao_ee_fill(AO_EE_CONFIG_BLOCK);
-               memcpy(buf, ao_ee_data, len);
-       } ao_mutex_put(&ao_ee_mutex);
-       return 1;
-}
-
-static void
-ee_dump(void)
-{
-       __xdata uint8_t b;
-       __xdata uint16_t block;
-       __xdata uint8_t i;
-
-       ao_cmd_hex();
-       block = ao_cmd_lex_i;
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       i = 0;
-       do {
-               if ((i & 7) == 0) {
-                       if (i)
-                               putchar('\n');
-                       ao_cmd_put16((uint16_t) i);
-               }
-               putchar(' ');
-               ao_ee_read(((uint32_t) block << 8) | i, &b, 1);
-               ao_cmd_put8(b);
-               ++i;
-       } while (i != 0);
-       putchar('\n');
-}
-
-static void
-ee_store(void)
-{
-       __xdata uint16_t block;
-       __xdata uint8_t i;
-       __xdata uint16_t len;
-       __xdata uint8_t b;
-       __xdata uint32_t addr;
-
-       ao_cmd_hex();
-       block = ao_cmd_lex_i;
-       ao_cmd_hex();
-       i = ao_cmd_lex_i;
-       addr = ((uint32_t) block << 8) | i;
-       ao_cmd_hex();
-       len = ao_cmd_lex_i;
-       if (ao_cmd_status != ao_cmd_success)
-               return;
-       while (len--) {
-               ao_cmd_hex();
-               if (ao_cmd_status != ao_cmd_success)
-                       return;
-               b = ao_cmd_lex_i;
-               ao_ee_write(addr, &b, 1);
-               addr++;
-       }
-       ao_ee_flush();
-}
-
-__code struct ao_cmds ao_ee_cmds[] = {
-       { 'e', ee_dump,         "e <block>                          Dump a block of EEPROM data" },
-       { 'w', ee_store,        "w <block> <start> <len> <data> ... Write data to EEPROM" },
-       { 0,   ee_store, NULL },
-};
-
-/*
- * To initialize the chip, set up the CS line and
- * the SPI interface
- */
-void
-ao_ee_init(void)
-{
-       /* set up CS */
-       EE_CS = 1;
-       P1DIR |= (1 << EE_CS_INDEX);
-       P1SEL &= ~(1 << EE_CS_INDEX);
-
-       /* Set up the USART pin assignment */
-       PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_2;
-
-       /* Ensure that USART0 takes precidence over USART1 for pins that
-        * they share
-        */
-       P2SEL = (P2SEL & ~P2SEL_PRI3P1_MASK) | P2SEL_PRI3P1_USART0;
-
-       /* Make the SPI pins be controlled by the USART peripheral */
-       P1SEL |= ((1 << 5) | (1 << 4) | (1 << 3));
-
-       /* Set up OUT DMA */
-       ao_ee_dma_out_id = ao_dma_alloc(&ao_ee_dma_out_done);
-
-       /* Set up IN DMA */
-       ao_ee_dma_in_id = ao_dma_alloc(&ao_ee_dma_in_done);
-
-       /* Set up the USART.
-        *
-        * SPI master mode
-        */
-       U0CSR = (UxCSR_MODE_SPI | UxCSR_RE | UxCSR_MASTER);
-
-       /* Set the baud rate and signal parameters
-        *
-        * The cc1111 is limited to a 24/8 MHz SPI clock,
-        * while the 25LC1024 is limited to 20MHz. So,
-        * use the 3MHz clock (BAUD_E 17, BAUD_M 0)
-        */
-       U0BAUD = 0;
-       U0GCR = (UxGCR_CPOL_NEGATIVE |
-                UxGCR_CPHA_FIRST_EDGE |
-                UxGCR_ORDER_MSB |
-                (17 << UxGCR_BAUD_E_SHIFT));
-       ao_cmd_register(&ao_ee_cmds[0]);
-}
diff --git a/src/ao_ee_fake.c b/src/ao_ee_fake.c
deleted file mode 100644 (file)
index b0c1d61..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-/*
- * For hardware without eeprom, the config code still
- * wants to call these functions
- */
-uint8_t
-ao_ee_write_config(uint8_t *buf, uint16_t len) __reentrant
-{
-       (void) buf;
-       (void) len;
-       return 1;
-}
-
-uint8_t
-ao_ee_read_config(uint8_t *buf, uint16_t len) __reentrant
-{
-       memset(buf, '\0', len);
-       return 1;
-}
diff --git a/src/ao_flight.c b/src/ao_flight.c
deleted file mode 100644 (file)
index be9b3bb..0000000
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef AO_FLIGHT_TEST
-#include "ao.h"
-#endif
-
-/* Main flight thread. */
-
-__pdata enum ao_flight_state   ao_flight_state;        /* current flight state */
-__pdata uint16_t               ao_flight_tick;         /* time of last data */
-__pdata uint16_t               ao_flight_prev_tick;    /* time of previous data */
-__pdata int16_t                        ao_flight_accel;        /* filtered acceleration */
-__pdata int16_t                        ao_flight_pres;         /* filtered pressure */
-__pdata int16_t                        ao_ground_pres;         /* startup pressure */
-__pdata int16_t                        ao_ground_accel;        /* startup acceleration */
-__pdata int16_t                        ao_min_pres;            /* minimum recorded pressure */
-__pdata uint16_t               ao_launch_tick;         /* time of launch detect */
-__pdata int16_t                        ao_main_pres;           /* pressure to eject main */
-
-/*
- * track min/max data over a long interval to detect
- * resting
- */
-__pdata uint16_t               ao_interval_end;
-__pdata int16_t                        ao_interval_cur_min_accel;
-__pdata int16_t                        ao_interval_cur_max_accel;
-__pdata int16_t                        ao_interval_cur_min_pres;
-__pdata int16_t                        ao_interval_cur_max_pres;
-__pdata int16_t                        ao_interval_min_accel;
-__pdata int16_t                        ao_interval_max_accel;
-__pdata int16_t                        ao_interval_min_pres;
-__pdata int16_t                        ao_interval_max_pres;
-
-__data uint8_t ao_flight_adc;
-__pdata int16_t ao_raw_accel, ao_raw_accel_prev, ao_raw_pres;
-
-/* Accelerometer calibration
- *
- * We're sampling the accelerometer through a resistor divider which
- * consists of 5k and 10k resistors. This multiplies the values by 2/3.
- * That goes into the cc1111 A/D converter, which is running at 11 bits
- * of precision with the bits in the MSB of the 16 bit value. Only positive
- * values are used, so values should range from 0-32752 for 0-3.3V. The
- * specs say we should see 40mV/g (uncalibrated), multiply by 2/3 for what
- * the A/D converter sees (26.67 mV/g). We should see 32752/3300 counts/mV,
- * for a final computation of:
- *
- * 26.67 mV/g * 32767/3300 counts/mV = 264.8 counts/g
- *
- * Zero g was measured at 16000 (we would expect 16384).
- * Note that this value is only require to tell if the
- * rocket is standing upright. Once that is determined,
- * the value of the accelerometer is averaged for 100 samples
- * to find the resting accelerometer value, which is used
- * for all further flight computations
- */
-
-#define GRAVITY 9.80665
-/* convert m/s to velocity count */
-#define VEL_MPS_TO_COUNT(mps) ((int32_t) (((mps) / GRAVITY) * ACCEL_G * 100))
-
-#define ACCEL_G                265
-#define ACCEL_ZERO_G   16000
-#define ACCEL_NOSE_UP  (ACCEL_G * 2 /3)
-#define ACCEL_BOOST    ACCEL_G * 2
-#define ACCEL_INT_LAND (ACCEL_G / 10)
-#define ACCEL_VEL_LAND VEL_MPS_TO_COUNT(10)
-#define ACCEL_VEL_MACH VEL_MPS_TO_COUNT(200)
-#define ACCEL_VEL_APOGEE       VEL_MPS_TO_COUNT(2)
-#define ACCEL_VEL_MAIN VEL_MPS_TO_COUNT(100)
-#define ACCEL_VEL_BOOST        VEL_MPS_TO_COUNT(5)
-
-/*
- * Barometer calibration
- *
- * We directly sample the barometer. The specs say:
- *
- * Pressure range: 15-115 kPa
- * Voltage at 115kPa: 2.82
- * Output scale: 27mV/kPa
- *
- * If we want to detect launch with the barometer, we need
- * a large enough bump to not be fooled by noise. At typical
- * launch elevations (0-2000m), a 200Pa pressure change cooresponds
- * to about a 20m elevation change. This is 5.4mV, or about 3LSB.
- * As all of our calculations are done in 16 bits, we'll actually see a change
- * of 16 times this though
- *
- * 27 mV/kPa * 32767 / 3300 counts/mV = 268.1 counts/kPa
- */
-
-#define BARO_kPa       268
-#define BARO_LAUNCH    (BARO_kPa / 5)  /* .2kPa, or about 20m */
-#define BARO_APOGEE    (BARO_kPa / 10) /* .1kPa, or about 10m */
-#define BARO_COAST     (BARO_kPa * 5)  /* 5kpa, or about 500m */
-#define BARO_MAIN      (BARO_kPa)      /* 1kPa, or about 100m */
-#define BARO_INT_LAND  (BARO_kPa / 20) /* .05kPa, or about 5m */
-#define BARO_LAND      (BARO_kPa * 10) /* 10kPa or about 1000m */
-
-/* We also have a clock, which can be used to sanity check things in
- * case of other failures
- */
-
-#define BOOST_TICKS_MAX        AO_SEC_TO_TICKS(15)
-
-/* This value is scaled in a weird way. It's a running total of accelerometer
- * readings minus the ground accelerometer reading. That means it measures
- * velocity, and quite accurately too. As it gets updated 100 times a second,
- * it's scaled by 100
- */
-__pdata int32_t        ao_flight_vel;
-__pdata int32_t ao_min_vel;
-__pdata int32_t        ao_old_vel;
-__pdata int16_t ao_old_vel_tick;
-__xdata int32_t ao_raw_accel_sum, ao_raw_pres_sum;
-
-/* Landing is detected by getting constant readings from both pressure and accelerometer
- * for a fairly long time (AO_INTERVAL_TICKS)
- */
-#define AO_INTERVAL_TICKS      AO_SEC_TO_TICKS(20)
-
-#define abs(a) ((a) < 0 ? -(a) : (a))
-
-void
-ao_flight(void)
-{
-       __pdata static uint16_t nsamples = 0;
-
-       ao_flight_adc = ao_adc_head;
-       ao_raw_accel_prev = 0;
-       ao_raw_accel = 0;
-       ao_raw_pres = 0;
-       ao_flight_tick = 0;
-       for (;;) {
-               ao_sleep(&ao_adc_ring);
-               while (ao_flight_adc != ao_adc_head) {
-                       __pdata uint8_t ticks;
-                       __pdata int16_t ao_vel_change;
-                       ao_flight_prev_tick = ao_flight_tick;
-
-                       /* Capture a sample */
-                       ao_raw_accel = ao_adc_ring[ao_flight_adc].accel;
-                       ao_raw_pres = ao_adc_ring[ao_flight_adc].pres;
-                       ao_flight_tick = ao_adc_ring[ao_flight_adc].tick;
-
-                       ao_flight_accel -= ao_flight_accel >> 4;
-                       ao_flight_accel += ao_raw_accel >> 4;
-                       ao_flight_pres -= ao_flight_pres >> 4;
-                       ao_flight_pres += ao_raw_pres >> 4;
-                       /* Update velocity
-                        *
-                        * The accelerometer is mounted so that
-                        * acceleration yields negative values
-                        * while deceleration yields positive values,
-                        * so subtract instead of add.
-                        */
-                       ticks = ao_flight_tick - ao_flight_prev_tick;
-                       ao_vel_change = (((ao_raw_accel >> 1) + (ao_raw_accel_prev >> 1)) - ao_ground_accel);
-                       ao_raw_accel_prev = ao_raw_accel;
-
-                       /* one is a common interval */
-                       if (ticks == 1)
-                               ao_flight_vel -= (int32_t) ao_vel_change;
-                       else
-                               ao_flight_vel -= (int32_t) ao_vel_change * (int32_t) ticks;
-
-                       ao_flight_adc = ao_adc_ring_next(ao_flight_adc);
-               }
-
-               if (ao_flight_pres < ao_min_pres)
-                       ao_min_pres = ao_flight_pres;
-               if (ao_flight_vel >= 0) {
-                       if (ao_flight_vel < ao_min_vel)
-                           ao_min_vel = ao_flight_vel;
-               } else {
-                       if (-ao_flight_vel < ao_min_vel)
-                           ao_min_vel = -ao_flight_vel;
-               }
-
-               switch (ao_flight_state) {
-               case ao_flight_startup:
-
-                       /* startup state:
-                        *
-                        * Collect 1000 samples of acceleration and pressure
-                        * data and average them to find the resting values
-                        */
-                       if (nsamples < 1000) {
-                               ao_raw_accel_sum += ao_raw_accel;
-                               ao_raw_pres_sum += ao_raw_pres;
-                               ++nsamples;
-                               continue;
-                       }
-                       ao_ground_accel = (ao_raw_accel_sum / nsamples);
-                       ao_ground_pres = (ao_raw_pres_sum / nsamples);
-                       ao_min_pres = ao_ground_pres;
-                       ao_config_get();
-                       ao_main_pres = ao_altitude_to_pres(ao_pres_to_altitude(ao_ground_pres) + ao_config.main_deploy);
-                       ao_flight_vel = 0;
-                       ao_min_vel = 0;
-                       ao_old_vel = ao_flight_vel;
-                       ao_old_vel_tick = ao_flight_tick;
-
-                       /* Go to pad state if the nose is pointing up */
-                       ao_config_get();
-                       if (ao_flight_accel < ao_config.accel_zero_g - ACCEL_NOSE_UP) {
-
-                               /* Disable the USB controller in flight mode
-                                * to save power
-                                */
-                               ao_usb_disable();
-
-                               /* Turn on telemetry system
-                                */
-                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_PAD);
-
-                               ao_flight_state = ao_flight_pad;
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                       } else {
-                               ao_flight_state = ao_flight_idle;
-
-                               /* Turn on the Green LED in idle mode
-                                */
-                               ao_led_on(AO_LED_GREEN);
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                       }
-                       /* signal successful initialization by turning off the LED */
-                       ao_led_off(AO_LED_RED);
-                       break;
-               case ao_flight_pad:
-
-                       /* Trim velocity
-                        *
-                        * Once a second, remove any velocity from
-                        * a second ago
-                        */
-                       if ((int16_t) (ao_flight_tick - ao_old_vel_tick) >= AO_SEC_TO_TICKS(1)) {
-                               ao_old_vel_tick = ao_flight_tick;
-                               ao_flight_vel -= ao_old_vel;
-                               ao_old_vel = ao_flight_vel;
-                       }
-                       /* pad to boost:
-                        *
-                        * accelerometer: > 2g AND velocity > 5m/s
-                        *             OR
-                        * barometer: > 20m vertical motion
-                        *
-                        * The accelerometer should always detect motion before
-                        * the barometer, but we use both to make sure this
-                        * transition is detected
-                        */
-                       if ((ao_flight_accel < ao_ground_accel - ACCEL_BOOST &&
-                            ao_flight_vel > ACCEL_VEL_BOOST) ||
-                           ao_flight_pres < ao_ground_pres - BARO_LAUNCH)
-                       {
-                               ao_flight_state = ao_flight_boost;
-                               ao_launch_tick = ao_flight_tick;
-
-                               /* start logging data */
-                               ao_log_start();
-
-                               /* Increase telemetry rate */
-                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_FLIGHT);
-
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                               break;
-                       }
-                       break;
-               case ao_flight_boost:
-
-                       /* boost to fast:
-                        *
-                        * accelerometer: start to fall at > 1/4 G
-                        *              OR
-                        * time: boost for more than 15 seconds
-                        *
-                        * Detects motor burn out by the switch from acceleration to
-                        * deceleration, or by waiting until the maximum burn duration
-                        * (15 seconds) has past.
-                        */
-                       if (ao_flight_accel > ao_ground_accel + (ACCEL_G >> 2) ||
-                           (int16_t) (ao_flight_tick - ao_launch_tick) > BOOST_TICKS_MAX)
-                       {
-                               ao_flight_state = ao_flight_fast;
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                               break;
-                       }
-                       break;
-               case ao_flight_fast:
-
-                       /* fast to coast:
-                        *
-                        * accelerometer: integrated velocity < 200 m/s
-                        *               OR
-                        * barometer: fall at least 500m from max altitude
-                        *
-                        * This extra state is required to avoid mis-detecting
-                        * apogee due to mach transitions.
-                        *
-                        * XXX this is essentially a single-detector test
-                        * as the 500m altitude change would likely result
-                        * in a loss of the rocket. More data on precisely
-                        * how big a pressure change the mach transition
-                        * generates would be useful here.
-                        */
-                       if (ao_flight_vel < ACCEL_VEL_MACH ||
-                           ao_flight_pres > ao_min_pres + BARO_COAST)
-                       {
-                               /* set min velocity to current velocity for
-                                * apogee detect
-                                */
-                               ao_min_vel = abs(ao_flight_vel);
-                               ao_flight_state = ao_flight_coast;
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                       }
-                       break;
-               case ao_flight_coast:
-
-                       /* apogee detect: coast to drogue deploy:
-                        *
-                        * accelerometer: abs(velocity) > min_velocity + 2m/s
-                        *               OR
-                        * barometer: fall at least 10m
-                        *
-                        * If the barometer saturates because the flight
-                        * goes over its measuring range (about 53k'),
-                        * requiring a 10m fall will avoid prematurely
-                        * detecting apogee; the accelerometer will take
-                        * over in that case and the integrated velocity
-                        * measurement should suffice to find apogee
-                        */
-                       if (/* abs(ao_flight_vel) > ao_min_vel + ACCEL_VEL_APOGEE || */
-                           ao_flight_pres > ao_min_pres + BARO_APOGEE)
-                       {
-                               /* ignite the drogue charge */
-                               ao_ignite(ao_igniter_drogue);
-
-                               /* slow down the telemetry system */
-                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_RECOVER);
-
-                               /* slow down the ADC sample rate */
-                               ao_timer_set_adc_interval(10);
-
-                               /*
-                                * Start recording min/max accel and pres for a while
-                                * to figure out when the rocket has landed
-                                */
-                               /* Set the 'last' limits to max range to prevent
-                                * early resting detection
-                                */
-                               ao_interval_min_accel = 0;
-                               ao_interval_max_accel = 0x7fff;
-                               ao_interval_min_pres = 0;
-                               ao_interval_max_pres = 0x7fff;
-
-                               /* initialize interval values */
-                               ao_interval_end = ao_flight_tick + AO_INTERVAL_TICKS;
-
-                               ao_interval_cur_min_pres = ao_interval_cur_max_pres = ao_flight_pres;
-                               ao_interval_cur_min_accel = ao_interval_cur_max_accel = ao_flight_accel;
-
-                               /* and enter drogue state */
-                               ao_flight_state = ao_flight_drogue;
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                       }
-
-                       break;
-               case ao_flight_drogue:
-
-                       /* drogue to main deploy:
-                        *
-                        * barometer: reach main deploy altitude
-                        *
-                        * Would like to use the accelerometer for this test, but
-                        * the orientation of the flight computer is unknown after
-                        * drogue deploy, so we ignore it. Could also detect
-                        * high descent rate using the pressure sensor to
-                        * recognize drogue deploy failure and eject the main
-                        * at that point. Perhaps also use the drogue sense lines
-                        * to notice continutity?
-                        */
-                       if (ao_flight_pres >= ao_main_pres)
-                       {
-                               ao_ignite(ao_igniter_main);
-                               ao_flight_state = ao_flight_main;
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                       }
-
-                       /* fall through... */
-               case ao_flight_main:
-
-                       /* drogue/main to land:
-                        *
-                        * accelerometer: value stable
-                        *                           AND
-                        * barometer: altitude stable and within 1000m of the launch altitude
-                        */
-
-                       if (ao_flight_pres < ao_interval_cur_min_pres)
-                               ao_interval_cur_min_pres = ao_flight_pres;
-                       if (ao_flight_pres > ao_interval_cur_max_pres)
-                               ao_interval_cur_max_pres = ao_flight_pres;
-                       if (ao_flight_accel < ao_interval_cur_min_accel)
-                               ao_interval_cur_min_accel = ao_flight_accel;
-                       if (ao_flight_accel > ao_interval_cur_max_accel)
-                               ao_interval_cur_max_accel = ao_flight_accel;
-
-                       if ((int16_t) (ao_flight_tick - ao_interval_end) >= 0) {
-                               ao_interval_max_pres = ao_interval_cur_max_pres;
-                               ao_interval_min_pres = ao_interval_cur_min_pres;
-                               ao_interval_max_accel = ao_interval_cur_max_accel;
-                               ao_interval_min_accel = ao_interval_cur_min_accel;
-                               ao_interval_end = ao_flight_tick + AO_INTERVAL_TICKS;
-                               ao_interval_cur_min_pres = ao_interval_cur_max_pres = ao_flight_pres;
-                               ao_interval_cur_min_accel = ao_interval_cur_max_accel = ao_flight_accel;
-                       }
-
-                       if ((uint16_t) (ao_interval_max_accel - ao_interval_min_accel) < (uint16_t) ACCEL_INT_LAND &&
-                           ao_flight_pres > ao_ground_pres - BARO_LAND &&
-                           (uint16_t) (ao_interval_max_pres - ao_interval_min_pres) < (uint16_t) BARO_INT_LAND)
-                       {
-                               ao_flight_state = ao_flight_landed;
-
-                               /* turn off the ADC capture */
-                               ao_timer_set_adc_interval(0);
-
-                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
-                       }
-                       break;
-               case ao_flight_landed:
-                       break;
-               }
-       }
-}
-
-#define AO_ACCEL_COUNT_TO_MSS(count)   ((count) / 27)
-#define AO_VEL_COUNT_TO_MS(count)      ((int16_t) ((count) / 2700))
-
-static void
-ao_flight_status(void)
-{
-       printf("STATE: %7s accel: %d speed: %d altitude: %d main: %d\n",
-              ao_state_names[ao_flight_state],
-              AO_ACCEL_COUNT_TO_MSS(ACCEL_ZERO_G - ao_flight_accel),
-              AO_VEL_COUNT_TO_MS(ao_flight_vel),
-              ao_pres_to_altitude(ao_flight_pres),
-              ao_pres_to_altitude(ao_main_pres));
-}
-
-static __xdata struct ao_task  flight_task;
-
-__code struct ao_cmds ao_flight_cmds[] = {
-       { 'f', ao_flight_status,        "f                                  Display current flight state" },
-       { 0, ao_flight_status, NULL }
-};
-
-void
-ao_flight_init(void)
-{
-       ao_flight_state = ao_flight_startup;
-       ao_interval_min_accel = 0;
-       ao_interval_max_accel = 0x7fff;
-       ao_interval_min_pres = 0;
-       ao_interval_max_pres = 0x7fff;
-       ao_interval_end = AO_INTERVAL_TICKS;
-
-       ao_add_task(&flight_task, ao_flight, "flight");
-       ao_cmd_register(&ao_flight_cmds[0]);
-}
diff --git a/src/ao_flight_test.c b/src/ao_flight_test.c
deleted file mode 100644 (file)
index 1466b88..0000000
+++ /dev/null
@@ -1,288 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#define _GNU_SOURCE
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define AO_ADC_RING    64
-#define ao_adc_ring_next(n)    (((n) + 1) & (AO_ADC_RING - 1))
-#define ao_adc_ring_prev(n)    (((n) - 1) & (AO_ADC_RING - 1))
-
-/*
- * One set of samples read from the A/D converter
- */
-struct ao_adc {
-       uint16_t        tick;           /* tick when the sample was read */
-       int16_t         accel;          /* accelerometer */
-       int16_t         pres;           /* pressure sensor */
-       int16_t         temp;           /* temperature sensor */
-       int16_t         v_batt;         /* battery voltage */
-       int16_t         sense_d;        /* drogue continuity sense */
-       int16_t         sense_m;        /* main continuity sense */
-};
-
-#define __pdata
-#define __data
-#define __xdata
-#define __code
-#define __reentrant
-
-enum ao_flight_state {
-       ao_flight_startup = 0,
-       ao_flight_idle = 1,
-       ao_flight_pad = 2,
-       ao_flight_boost = 3,
-       ao_flight_fast = 4,
-       ao_flight_coast = 5,
-       ao_flight_drogue = 6,
-       ao_flight_main = 7,
-       ao_flight_landed = 8,
-       ao_flight_invalid = 9
-};
-
-struct ao_adc ao_adc_ring[AO_ADC_RING];
-uint8_t ao_adc_head;
-
-#define ao_led_on(l)
-#define ao_led_off(l)
-#define ao_timer_set_adc_interval(i)
-#define ao_wakeup(wchan) ao_dump_state()
-#define ao_cmd_register(c)
-#define ao_usb_disable()
-#define ao_telemetry_set_interval(x)
-
-enum ao_igniter {
-       ao_igniter_drogue = 0,
-       ao_igniter_main = 1
-};
-
-void
-ao_ignite(enum ao_igniter igniter)
-{
-       printf ("ignite %s\n", igniter == ao_igniter_drogue ? "drogue" : "main");
-}
-
-struct ao_task {
-       int dummy;
-};
-
-#define ao_add_task(t,f,n)
-
-#define ao_log_start()
-#define ao_log_stop()
-
-#define AO_MS_TO_TICKS(ms)     ((ms) / 10)
-#define AO_SEC_TO_TICKS(s)     ((s) * 100)
-
-#define AO_FLIGHT_TEST
-
-struct ao_adc ao_adc_static;
-
-FILE *emulator_in;
-
-void
-ao_dump_state(void);
-
-void
-ao_sleep(void *wchan);
-
-const char const * const ao_state_names[] = {
-       "startup", "idle", "pad", "boost", "fast",
-       "coast", "drogue", "main", "landed", "invalid"
-};
-
-struct ao_cmds {
-       char            cmd;
-       void            (*func)(void);
-       const char      *help;
-};
-
-
-static int16_t altitude_table[2048] = {
-#include "altitude.h"
-};
-
-int16_t
-ao_pres_to_altitude(int16_t pres) __reentrant
-{
-       pres = pres >> 4;
-       if (pres < 0) pres = 0;
-       if (pres > 2047) pres = 2047;
-       return altitude_table[pres];
-}
-
-int16_t
-ao_altitude_to_pres(int16_t alt) __reentrant
-{
-       int16_t pres;
-
-       for (pres = 0; pres < 2047; pres++)
-               if (altitude_table[pres] <= alt)
-                       break;
-       return pres << 4;
-}
-
-struct ao_config {
-       uint16_t        main_deploy;
-       int16_t         accel_zero_g;
-};
-
-#define ao_config_get()
-
-struct ao_config ao_config = { 250, 16000 };
-
-#include "ao_flight.c"
-
-void
-ao_insert(void)
-{
-       ao_adc_ring[ao_adc_head] = ao_adc_static;
-       ao_adc_head = ao_adc_ring_next(ao_adc_head);
-       if (ao_flight_state != ao_flight_startup) {
-               printf("time %g accel %d pres %d\n",
-                      (double) ao_adc_static.tick / 100,
-                      ao_adc_static.accel,
-                      ao_adc_static.pres);
-       }
-}
-
-static int     ao_records_read = 0;
-static int     ao_eof_read = 0;
-static int     ao_flight_ground_accel;
-static int     ao_flight_started = 0;
-
-void
-ao_sleep(void *wchan)
-{
-       ao_dump_state();
-       if (wchan == &ao_adc_ring) {
-               char            type;
-               uint16_t        tick;
-               uint16_t        a, b;
-               int             ret;
-               char            line[1024];
-               char            *saveptr;
-               char            *l;
-               char            *words[64];
-               int             nword;
-
-               for (;;) {
-                       if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
-                       {
-                               ao_adc_static.accel = ao_flight_ground_accel;
-                               ao_insert();
-                               return;
-                       }
-
-                       if (!fgets(line, sizeof (line), emulator_in)) {
-                               if (++ao_eof_read >= 1000) {
-                                       printf ("no more data, exiting simulation\n");
-                                       exit(0);
-                               }
-                               ao_adc_static.tick += 10;
-                               ao_insert();
-                               return;
-                       }
-                       l = line;
-                       for (nword = 0; nword < 64; nword++) {
-                               words[nword] = strtok_r(l, " \t\n", &saveptr);
-                               l = NULL;
-                               if (words[nword] == NULL)
-                                       break;
-                       }
-                       if (nword == 4) {
-                               type = words[0][0];
-                               tick = strtoul(words[1], NULL, 16);
-                               a = strtoul(words[2], NULL, 16);
-                               b = strtoul(words[2], NULL, 16);
-                       } else if (nword >= 36 && strcmp(words[0], "CALL") == 0) {
-                               tick = atoi(words[10]);
-                               if (!ao_flight_started) {
-                                       type = 'F';
-                                       a = atoi(words[26]);
-                                       ao_flight_started = 1;
-                               } else {
-                                       type = 'A';
-                                       a = atoi(words[12]);
-                                       b = atoi(words[14]);
-                               }
-                       }
-                       if (type != 'F' && !ao_flight_started)
-                               continue;
-
-                       switch (type) {
-                       case 'F':
-                               ao_flight_ground_accel = a;
-                               ao_flight_started = 1;
-                               break;
-                       case 'S':
-                               break;
-                       case 'A':
-                               ao_adc_static.tick = tick;
-                               ao_adc_static.accel = a;
-                               ao_adc_static.pres = b;
-                               ao_records_read++;
-                               ao_insert();
-                               return;
-                       case 'T':
-                               ao_adc_static.tick = tick;
-                               ao_adc_static.temp = a;
-                               ao_adc_static.v_batt = b;
-                               break;
-                       case 'D':
-                       case 'G':
-                       case 'N':
-                       case 'W':
-                       case 'H':
-                               break;
-                       }
-               }
-
-       }
-}
-#define COUNTS_PER_G 264.8
-
-void
-ao_dump_state(void)
-{
-       if (ao_flight_state == ao_flight_startup)
-               return;
-       printf ("\t\t\t\t\t%s accel %g vel %g alt %d main %d\n",
-               ao_state_names[ao_flight_state],
-               (ao_ground_accel - ao_flight_accel) / COUNTS_PER_G * GRAVITY,
-               (double) ao_flight_vel / 100 / COUNTS_PER_G * GRAVITY,
-               ao_pres_to_altitude(ao_flight_pres) - ao_pres_to_altitude(ao_ground_pres),
-               ao_pres_to_altitude(ao_main_pres) - ao_pres_to_altitude(ao_ground_pres));
-       if (ao_flight_state == ao_flight_landed)
-               exit(0);
-}
-
-int
-main (int argc, char **argv)
-{
-       emulator_in = fopen (argv[1], "r");
-       if (!emulator_in) {
-               perror(argv[1]);
-               exit(1);
-       }
-       ao_flight_init();
-       ao_flight();
-}
diff --git a/src/ao_gps.c b/src/ao_gps.c
deleted file mode 100644 (file)
index c4c434f..0000000
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef AO_GPS_TEST
-#include "ao.h"
-#endif
-
-__xdata uint8_t ao_gps_mutex;
-__xdata struct ao_gps_data     ao_gps_data;
-
-static const char ao_gps_set_nmea[] = "\r\n$PSRF100,0,57600,8,1,0*37\r\n";
-
-const char ao_gps_config[] = {
-
-       0xa0, 0xa2, 0x00, 0x0e, /* length: 14 bytes */
-       136,                    /* mode control */
-       0, 0,                   /* reserved */
-       4,                      /* degraded mode (disabled) */
-       0, 0,                   /* reserved */
-       0, 0,                   /* user specified altitude */
-       2,                      /* alt hold mode (disabled, require 3d fixes) */
-       0,                      /* alt hold source (use last computed altitude) */
-       0,                      /* reserved */
-       0,                      /* Degraded time out (disabled) */
-       0,                      /* Dead Reckoning time out (disabled) */
-       0,                      /* Track smoothing (disabled) */
-       0x00, 0x8e, 0xb0, 0xb3,
-
-       0xa0, 0xa2, 0x00, 0x08, /* length: 8 bytes */
-       166,                    /* Set message rate */
-       2,                      /* enable/disable all messages */
-       0,                      /* message id (ignored) */
-       0,                      /* update rate (0 = disable) */
-       0, 0, 0, 0,             /* reserved */
-       0x00, 0xa8, 0xb0, 0xb3,
-
-       0xa0, 0xa2, 0x00, 0x02, /* length: 2 bytes */
-       143,                    /* static navigation */
-       0,                      /* disable */
-       0x00, 0x8f, 0xb0, 0xb3,
-};
-
-#define NAV_TYPE_GPS_FIX_TYPE_MASK                     (7 << 0)
-#define NAV_TYPE_NO_FIX                                        (0 << 0)
-#define NAV_TYPE_SV_KF                                 (1 << 0)
-#define NAV_TYPE_2_SV_KF                               (2 << 0)
-#define NAV_TYPE_3_SV_KF                               (3 << 0)
-#define NAV_TYPE_4_SV_KF                               (4 << 0)
-#define NAV_TYPE_2D_LEAST_SQUARES                      (5 << 0)
-#define NAV_TYPE_3D_LEAST_SQUARES                      (6 << 0)
-#define NAV_TYPE_DR                                    (7 << 0)
-#define NAV_TYPE_TRICKLE_POWER                         (1 << 3)
-#define NAV_TYPE_ALTITUDE_HOLD_MASK                    (3 << 4)
-#define NAV_TYPE_ALTITUDE_HOLD_NONE                    (0 << 4)
-#define NAV_TYPE_ALTITUDE_HOLD_KF                      (1 << 4)
-#define NAV_TYPE_ALTITUDE_HOLD_USER                    (2 << 4)
-#define NAV_TYPE_ALTITUDE_HOLD_ALWAYS                  (3 << 4)
-#define NAV_TYPE_DOP_LIMIT_EXCEEDED                    (1 << 6)
-#define NAV_TYPE_DGPS_APPLIED                          (1 << 7)
-#define NAV_TYPE_SENSOR_DR                             (1 << 8)
-#define NAV_TYPE_OVERDETERMINED                                (1 << 9)
-#define NAV_TYPE_DR_TIMEOUT_EXCEEDED                   (1 << 10)
-#define NAV_TYPE_FIX_MI_EDIT                           (1 << 11)
-#define NAV_TYPE_INVALID_VELOCITY                      (1 << 12)
-#define NAV_TYPE_ALTITUDE_HOLD_DISABLED                        (1 << 13)
-#define NAV_TYPE_DR_ERROR_STATUS_MASK                  (3 << 14)
-#define NAV_TYPE_DR_ERROR_STATUS_GPS_ONLY              (0 << 14)
-#define NAV_TYPE_DR_ERROR_STATUS_DR_FROM_GPS           (1 << 14)
-#define NAV_TYPE_DR_ERROR_STATUS_DR_SENSOR_ERROR       (2 << 14)
-#define NAV_TYPE_DR_ERROR_STATUS_DR_IN_TEST            (3 << 14)
-
-struct sirf_geodetic_nav_data {
-       uint16_t        nav_type;
-       uint16_t        utc_year;
-       uint8_t         utc_month;
-       uint8_t         utc_day;
-       uint8_t         utc_hour;
-       uint8_t         utc_minute;
-       uint16_t        utc_second;
-       int32_t         lat;
-       int32_t         lon;
-       int32_t         alt_msl;
-       uint16_t        ground_speed;
-       uint16_t        course;
-       int16_t         climb_rate;
-       uint32_t        h_error;
-       uint32_t        v_error;
-       uint8_t         num_sv;
-       uint8_t         hdop;
-};
-
-static __xdata struct sirf_geodetic_nav_data   ao_sirf_data;
-
-static __pdata uint16_t ao_sirf_cksum;
-static __pdata uint16_t ao_sirf_len;
-
-#define ao_sirf_byte() ((uint8_t) ao_serial_getchar())
-
-static uint8_t data_byte(void)
-{
-       uint8_t c = ao_sirf_byte();
-       --ao_sirf_len;
-       ao_sirf_cksum += c;
-       return c;
-}
-
-static void sirf_u16(uint8_t offset)
-{
-       uint16_t __xdata *ptr = (uint16_t __xdata *) (((char __xdata *) &ao_sirf_data) + offset);
-       uint16_t val;
-
-       val = data_byte() << 8;
-       val |= data_byte ();
-       *ptr = val;
-}
-
-static void sirf_u8(uint8_t offset)
-{
-       uint8_t __xdata *ptr = (uint8_t __xdata *) (((char __xdata *) &ao_sirf_data) + offset);
-       uint8_t val;
-
-       val = data_byte ();
-       *ptr = val;
-}
-
-static void sirf_u32(uint8_t offset)
-{
-       uint32_t __xdata *ptr = (uint32_t __xdata *) (((char __xdata *) &ao_sirf_data) + offset);
-       uint32_t val;
-
-       val = ((uint32_t) data_byte ()) << 24;
-       val |= ((uint32_t) data_byte ()) << 16;
-       val |= ((uint32_t) data_byte ()) << 8;
-       val |= ((uint32_t) data_byte ());
-       *ptr = val;
-}
-
-static void sirf_discard(uint8_t len)
-{
-       while (len--)
-               data_byte();
-}
-
-#define SIRF_END       0
-#define SIRF_DISCARD   1
-#define SIRF_U8                2
-#define SIRF_U16       3
-#define SIRF_U32       4
-
-struct sirf_packet_parse {
-       uint8_t type;
-       uint8_t offset;
-};
-
-static const struct sirf_packet_parse geodetic_nav_data_packet[] = {
-       { SIRF_DISCARD, 2 },                                                    /* 1 nav valid */
-       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, nav_type) },        /* 3 */
-       { SIRF_DISCARD, 6 },                                                    /* 5 week number, time of week */
-       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, utc_year) },        /* 11 */
-       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_month) },        /* 13 */
-       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_day) },          /* 14 */
-       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_hour) },         /* 15 */
-       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_minute) },       /* 16 */
-       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, utc_second) },      /* 17 */
-       { SIRF_DISCARD, 4 },    /* satellite id list */                         /* 19 */
-       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, lat) },             /* 23 */
-       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, lon) },             /* 27 */
-       { SIRF_DISCARD, 4 },    /* altitude from ellipsoid */                   /* 31 */
-       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, alt_msl) },         /* 35 */
-       { SIRF_DISCARD, 1 },    /* map datum */                                 /* 39 */
-       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, ground_speed) },    /* 40 */
-       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, course) },          /* 42 */
-       { SIRF_DISCARD, 2 },    /* magnetic variation */                        /* 44 */
-       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, climb_rate) },      /* 46 */
-       { SIRF_DISCARD, 2 },    /* turn rate */                                 /* 48 */
-       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, h_error) },         /* 50 */
-       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, v_error) },         /* 54 */
-       { SIRF_DISCARD, 30 },   /* time error, h_vel error, clock_bias,
-                                  clock bias error, clock drift,
-                                  clock drift error, distance,
-                                  distance error, heading error */             /* 58 */
-       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, num_sv) },           /* 88 */
-       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, hdop) },             /* 89 */
-       { SIRF_DISCARD, 1 },    /* additional mode info */                      /* 90 */
-       { SIRF_END, 0 },                                                        /* 91 */
-};
-
-static void
-ao_sirf_parse_41(void)
-{
-       uint8_t i, offset;
-
-       for (i = 0; ; i++) {
-               offset = geodetic_nav_data_packet[i].offset;
-               switch (geodetic_nav_data_packet[i].type) {
-               case SIRF_END:
-                       return;
-               case SIRF_DISCARD:
-                       sirf_discard(offset);
-                       break;
-               case SIRF_U8:
-                       sirf_u8(offset);
-                       break;
-               case SIRF_U16:
-                       sirf_u16(offset);
-                       break;
-               case SIRF_U32:
-                       sirf_u32(offset);
-                       break;
-               }
-       }
-}
-
-static void
-ao_gps_setup(void) __reentrant
-{
-       uint8_t i, k;
-       ao_serial_set_speed(AO_SERIAL_SPEED_4800);
-       for (i = 0; i < 64; i++)
-               ao_serial_putchar(0x00);
-       for (k = 0; k < 3; k++)
-               for (i = 0; i < sizeof (ao_gps_set_nmea); i++)
-                       ao_serial_putchar(ao_gps_set_nmea[i]);
-       ao_serial_set_speed(AO_SERIAL_SPEED_57600);
-       for (i = 0; i < 64; i++)
-               ao_serial_putchar(0x00);
-}
-
-static const char ao_gps_set_message_rate[] = {
-       0xa0, 0xa2, 0x00, 0x08,
-       166,
-       0,
-};
-
-void
-ao_sirf_set_message_rate(uint8_t msg, uint8_t rate)
-{
-       uint16_t        cksum = 0x00a6;
-       uint8_t         i;
-
-       for (i = 0; i < sizeof (ao_gps_set_message_rate); i++)
-               ao_serial_putchar(ao_gps_set_message_rate[i]);
-       ao_serial_putchar(msg);
-       ao_serial_putchar(rate);
-       cksum = 0xa6 + msg + rate;
-       for (i = 0; i < 4; i++)
-               ao_serial_putchar(0);
-       ao_serial_putchar((cksum >> 8) & 0x7f);
-       ao_serial_putchar(cksum & 0xff);
-       ao_serial_putchar(0xb0);
-       ao_serial_putchar(0xb3);
-}
-
-static const uint8_t sirf_disable[] = {
-       2,
-       4,
-       9,
-       10,
-       27,
-       50,
-       52,
-};
-
-void
-ao_gps(void) __reentrant
-{
-       uint8_t i, k;
-       uint16_t cksum;
-
-       ao_gps_setup();
-       for (k = 0; k < 5; k++)
-       {
-               for (i = 0; i < sizeof (ao_gps_config); i++)
-                       ao_serial_putchar(ao_gps_config[i]);
-               for (i = 0; i < sizeof (sirf_disable); i++)
-                       ao_sirf_set_message_rate(sirf_disable[i], 0);
-               ao_sirf_set_message_rate(41, 1);
-       }
-       for (;;) {
-               /* Locate the begining of the next record */
-               while (ao_sirf_byte() != (uint8_t) 0xa0)
-                       ;
-               if (ao_sirf_byte() != (uint8_t) 0xa2)
-                       continue;
-
-               /* Length */
-               ao_sirf_len = ao_sirf_byte() << 8;
-               ao_sirf_len |= ao_sirf_byte();
-               if (ao_sirf_len > 1023)
-                       continue;
-
-               ao_sirf_cksum = 0;
-
-               /* message ID */
-               i = data_byte ();                                                       /* 0 */
-
-               switch (i) {
-               case 41:
-                       if (ao_sirf_len < 90)
-                               break;
-                       ao_sirf_parse_41();
-                       break;
-               }
-               if (ao_sirf_len != 0)
-                       continue;
-
-               /* verify checksum and end sequence */
-               ao_sirf_cksum &= 0x7fff;
-               cksum = ao_sirf_byte() << 8;
-               cksum |= ao_sirf_byte();
-               if (ao_sirf_cksum != cksum)
-                       continue;
-               if (ao_sirf_byte() != (uint8_t) 0xb0)
-                       continue;
-               if (ao_sirf_byte() != (uint8_t) 0xb3)
-                       continue;
-
-               switch (i) {
-               case 41:
-                       ao_mutex_get(&ao_gps_mutex);
-                       ao_gps_data.hour = ao_sirf_data.utc_hour;
-                       ao_gps_data.minute = ao_sirf_data.utc_minute;
-                       ao_gps_data.second = ao_sirf_data.utc_second / 1000;
-                       ao_gps_data.flags = ((ao_sirf_data.num_sv << AO_GPS_NUM_SAT_SHIFT) & AO_GPS_NUM_SAT_MASK) | AO_GPS_RUNNING;
-                       if ((ao_sirf_data.nav_type & NAV_TYPE_GPS_FIX_TYPE_MASK) >= NAV_TYPE_4_SV_KF)
-                               ao_gps_data.flags |= AO_GPS_VALID;
-                       ao_gps_data.latitude = ao_sirf_data.lat;
-                       ao_gps_data.longitude = ao_sirf_data.lon;
-                       ao_gps_data.altitude = ao_sirf_data.alt_msl / 100;
-                       ao_gps_data.ground_speed = ao_sirf_data.ground_speed;
-                       ao_gps_data.course = ao_sirf_data.course / 200;
-                       ao_gps_data.hdop = ao_sirf_data.hdop;
-                       ao_gps_data.climb_rate = ao_sirf_data.climb_rate;
-                       if (ao_sirf_data.h_error > 6553500)
-                               ao_gps_data.h_error = 65535;
-                       else
-                               ao_gps_data.h_error = ao_sirf_data.h_error / 100;
-                       if (ao_sirf_data.v_error > 6553500)
-                               ao_gps_data.v_error = 65535;
-                       else
-                               ao_gps_data.v_error = ao_sirf_data.v_error / 100;
-                       ao_mutex_put(&ao_gps_mutex);
-                       ao_wakeup(&ao_gps_data);
-                       break;
-               }
-       }
-}
-
-__xdata struct ao_task ao_gps_task;
-
-static void
-gps_dump(void) __reentrant
-{
-       ao_mutex_get(&ao_gps_mutex);
-       ao_gps_print(&ao_gps_data);
-       ao_mutex_put(&ao_gps_mutex);
-}
-
-__code struct ao_cmds ao_gps_cmds[] = {
-       { 'g', gps_dump,        "g                                  Display current GPS values" },
-       { 0, gps_dump, NULL },
-};
-
-void
-ao_gps_init(void)
-{
-       ao_add_task(&ao_gps_task, ao_gps, "gps");
-       ao_cmd_register(&ao_gps_cmds[0]);
-}
diff --git a/src/ao_gps_print.c b/src/ao_gps_print.c
deleted file mode 100644 (file)
index 8cc07c8..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef AO_GPS_TEST
-#include "ao.h"
-#endif
-
-struct ao_gps_split {
-       uint8_t positive;
-       uint8_t degrees;
-       uint8_t minutes;
-       uint16_t minutes_fraction;
-};
-
-static void
-ao_gps_split(int32_t v, __xdata struct ao_gps_split *split) __reentrant
-{
-       uint32_t minutes_e7;
-
-       split->positive = 1;
-       if (v < 0) {
-               v = -v;
-               split->positive = 0;
-       }
-       split->degrees = v / 10000000;
-       minutes_e7 = (v % 10000000) * 60;
-       split->minutes = minutes_e7 / 10000000;
-       split->minutes_fraction = (minutes_e7 % 10000000) / 1000;
-}
-
-void
-ao_gps_print(__xdata struct ao_gps_data *gps_data) __reentrant
-{
-       printf("GPS %2d sat",
-              (gps_data->flags & AO_GPS_NUM_SAT_MASK) >> AO_GPS_NUM_SAT_SHIFT);
-       if (gps_data->flags & AO_GPS_VALID) {
-               static __xdata struct ao_gps_split      lat, lon;
-               int16_t climb, climb_int, climb_frac;
-
-               ao_gps_split(gps_data->latitude, &lat);
-               ao_gps_split(gps_data->longitude, &lon);
-               printf(" %2d:%02d:%02d",
-                      gps_data->hour,
-                      gps_data->minute,
-                      gps_data->second);
-               printf(" %2d°%02d.%04d'%c %2d°%02d.%04d'%c %5dm",
-                      lat.degrees,
-                      lat.minutes,
-                      lat.minutes_fraction,
-                      lat.positive ? 'N' : 'S',
-                      lon.degrees,
-                      lon.minutes,
-                      lon.minutes_fraction,
-                      lon.positive ? 'E' : 'W',
-                      gps_data->altitude);
-               climb = gps_data->climb_rate;
-               if (climb >= 0) {
-                       climb_int = climb / 100;
-                       climb_frac = climb % 100;
-               } else {
-                       climb = -climb;
-                       climb_int = -(climb / 100);
-                       climb_frac = climb % 100;
-               }
-               printf(" %5u.%02dm/s(H) %d° %5d.%02dm/s(V)",
-                      gps_data->ground_speed / 100,
-                      gps_data->ground_speed % 100,
-                      gps_data->course * 2,
-                      climb / 100,
-                      climb % 100);
-               printf(" %d.%d(hdop) %5u(herr) %5u(verr)\n",
-                      gps_data->hdop / 5,
-                      (gps_data->hdop * 2) % 10,
-                      gps_data->h_error,
-                      gps_data->v_error);
-       } else if (gps_data->flags & AO_GPS_RUNNING) {
-               printf(" unlocked\n");
-       } else {
-               printf (" not-connected\n");
-       }
-}
diff --git a/src/ao_gps_report.c b/src/ao_gps_report.c
deleted file mode 100644 (file)
index dce12ad..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-void
-ao_gps_report(void)
-{
-       static __xdata struct ao_log_record     gps_log;
-       static __xdata struct ao_gps_data       gps_data;
-
-       for (;;) {
-               ao_sleep(&ao_gps_data);
-               ao_mutex_get(&ao_gps_mutex);
-               memcpy(&gps_data, &ao_gps_data, sizeof (struct ao_gps_data));
-               ao_mutex_put(&ao_gps_mutex);
-
-               if (!(gps_data.flags & AO_GPS_VALID))
-                       continue;
-
-               gps_log.tick = ao_time();
-               gps_log.type = AO_LOG_GPS_TIME;
-               gps_log.u.gps_time.hour = gps_data.hour;
-               gps_log.u.gps_time.minute = gps_data.minute;
-               gps_log.u.gps_time.second = gps_data.second;
-               gps_log.u.gps_time.flags = gps_data.flags;
-               ao_log_data(&gps_log);
-               gps_log.type = AO_LOG_GPS_LAT;
-               gps_log.u.gps_latitude = gps_data.latitude;
-               ao_log_data(&gps_log);
-               gps_log.type = AO_LOG_GPS_LON;
-               gps_log.u.gps_longitude = gps_data.longitude;
-               ao_log_data(&gps_log);
-               gps_log.type = AO_LOG_GPS_ALT;
-               gps_log.u.gps_altitude.altitude = gps_data.altitude;
-               gps_log.u.gps_altitude.unused = 0xffff;
-               ao_log_data(&gps_log);
-       }
-}
-
-__xdata struct ao_task ao_gps_report_task;
-
-void
-ao_gps_report_init(void)
-{
-       ao_add_task(&ao_gps_report_task, ao_gps_report, "gps_report");
-}
diff --git a/src/ao_gps_test.c b/src/ao_gps_test.c
deleted file mode 100644 (file)
index fb9b0d1..0000000
+++ /dev/null
@@ -1,373 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#define AO_GPS_TEST
-#include "ao_host.h"
-#include <termios.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#define AO_GPS_NUM_SAT_MASK    (0xf << 0)
-#define AO_GPS_NUM_SAT_SHIFT   (0)
-
-#define AO_GPS_VALID           (1 << 4)
-#define AO_GPS_RUNNING         (1 << 5)
-
-struct ao_gps_data {
-       uint8_t                 hour;
-       uint8_t                 minute;
-       uint8_t                 second;
-       uint8_t                 flags;
-       int32_t                 latitude;       /* degrees * 10⁷ */
-       int32_t                 longitude;      /* degrees * 10⁷ */
-       int16_t                 altitude;       /* m */
-       uint16_t                ground_speed;   /* cm/s */
-       uint8_t                 course;         /* degrees / 2 */
-       uint8_t                 hdop;           /* * 5 */
-       int16_t                 climb_rate;     /* cm/s */
-       uint16_t                h_error;        /* m */
-       uint16_t                v_error;        /* m */
-};
-
-void
-ao_mutex_get(uint8_t *mutex)
-{
-}
-
-void
-ao_mutex_put(uint8_t *mutex)
-{
-}
-
-static int
-ao_gps_fd;
-
-static void
-ao_dbg_char(char c)
-{
-       char    line[128];
-       line[0] = '\0';
-       if (c < ' ') {
-               if (c == '\n')
-                       sprintf (line, "\n");
-               else
-                       sprintf (line, "\\%02x", ((int) c) & 0xff);
-       } else {
-               sprintf (line, "%c", c);
-       }
-       write(1, line, strlen(line));
-}
-
-#define QUEUE_LEN      4096
-
-static char    input_queue[QUEUE_LEN];
-int            input_head, input_tail;
-
-
-static void
-check_sirf_message(char *from, uint8_t *msg, int len)
-{
-       uint16_t        encoded_len, encoded_cksum;
-       uint16_t        cksum;
-       uint8_t         id;
-       int             i;
-
-       if (msg[0] != 0xa0 || msg[1] != 0xa2) {
-               printf ("bad header\n");
-               return;
-       }
-       if (len < 7) {
-               printf("short\n");
-               return;
-       }
-       if (msg[len-1] != 0xb3 || msg[len-2] != 0xb0) {
-               printf ("bad trailer\n");
-               return;
-       }
-       encoded_len = (msg[2] << 8) | msg[3];
-       id = msg[4];
-       if (encoded_len != len - 8) {
-               printf ("length mismatch (got %d, wanted %d)\n",
-                       len - 8, encoded_len);
-               return;
-       }
-       encoded_cksum = (msg[len - 4] << 8) | msg[len-3];
-       cksum = 0;
-       for (i = 4; i < len - 4; i++)
-               cksum = (cksum + msg[i]) & 0x7fff;
-       if (encoded_cksum != cksum) {
-               printf ("cksum mismatch (got %04x wanted %04x)\n",
-                       cksum, encoded_cksum);
-               return;
-       }
-       id = msg[4];
-       if (id == 41) {
-               int     off = 4;
-
-               uint8_t         id;
-               uint16_t        nav_valid;
-               uint16_t        nav_type;
-               uint16_t        week;
-               uint32_t        tow;
-               uint16_t        year;
-               uint8_t         month;
-               uint8_t         day;
-               uint8_t         hour;
-               uint8_t         minute;
-               uint16_t        second;
-               uint32_t        sat_list;
-               int32_t         lat;
-               int32_t         lon;
-               int32_t         alt_ell;
-               int32_t         alt_msl;
-               int8_t          datum;
-               uint16_t        sog;
-               uint16_t        cog;
-               int16_t         mag_var;
-               int16_t         climb_rate;
-               int16_t         heading_rate;
-               uint32_t        h_error;
-               uint32_t        v_error;
-               uint32_t        t_error;
-               uint16_t        h_v_error;
-
-#define get_u8(u)      u = (msg[off]); off+= 1
-#define get_u16(u)     u = (msg[off] << 8) | (msg[off + 1]); off+= 2
-#define get_u32(u)     u = (msg[off] << 24) | (msg[off + 1] << 16) | (msg[off+2] << 8) | (msg[off+3]); off+= 4
-
-               get_u8(id);
-               get_u16(nav_valid);
-               get_u16(nav_type);
-               get_u16(week);
-               get_u32(tow);
-               get_u16(year);
-               get_u8(month);
-               get_u8(day);
-               get_u8(hour);
-               get_u8(minute);
-               get_u16(second);
-               get_u32(sat_list);
-               get_u32(lat);
-               get_u32(lon);
-               get_u32(alt_ell);
-               get_u32(alt_msl);
-               get_u8(datum);
-               get_u16(sog);
-               get_u16(cog);
-               get_u16(mag_var);
-               get_u16(climb_rate);
-               get_u16(heading_rate);
-               get_u32(h_error);
-               get_u32(v_error);
-               get_u32(t_error);
-               get_u16(h_v_error);
-
-
-               printf ("Geodetic Navigation Data (41):\n");
-               printf ("\tNav valid %04x\n", nav_valid);
-               printf ("\tNav type %04x\n", nav_type);
-               printf ("\tWeek %d\n", week);
-               printf ("\tTOW %d\n", tow);
-               printf ("\tyear %d\n", year);
-               printf ("\tmonth %d\n", month);
-               printf ("\tday %d\n", day);
-               printf ("\thour %d\n", hour);
-               printf ("\tminute %d\n", minute);
-               printf ("\tsecond %g\n", second / 1000.0);
-               printf ("\tsats: %08x\n", sat_list);
-               printf ("\tlat: %g\n", lat / 1.0e7);
-               printf ("\tlon: %g\n", lon / 1.0e7);
-               printf ("\talt_ell: %g\n", alt_ell / 100.0);
-               printf ("\talt_msll: %g\n", alt_msl / 100.0);
-               printf ("\tdatum: %d\n", datum);
-               printf ("\tground speed: %g\n", sog / 100.0);
-               printf ("\tcourse: %g\n", cog / 100.0);
-               printf ("\tclimb: %g\n", climb_rate / 100.0);
-               printf ("\theading rate: %g\n", heading_rate / 100.0);
-               printf ("\th error: %g\n", h_error / 100.0);
-               printf ("\tv error: %g\n", v_error / 100.0);
-               printf ("\tt error: %g\n", t_error / 100.0);
-               printf ("\th vel error: %g\n", h_v_error / 100.0);
-       } else {
-               return;
-               printf ("%s %4d:", from, encoded_len);
-               for (i = 4; i < len - 4; i++) {
-                       if (((i - 4) & 0xf) == 0)
-                               printf("\n   ");
-                       printf (" %3d", msg[i]);
-               }
-               printf ("\n");
-       }
-}
-
-static uint8_t sirf_message[4096];
-static int     sirf_message_len;
-static uint8_t sirf_in_message[4096];
-static int     sirf_in_len;
-
-char
-ao_serial_getchar(void)
-{
-       char    c;
-       uint8_t uc;
-
-       while (input_head == input_tail) {
-               for (;;) {
-                       input_tail = read(ao_gps_fd, input_queue, QUEUE_LEN);
-                       if (input_tail < 0) {
-                               if (errno == EINTR || errno == EAGAIN)
-                                       continue;
-                               perror ("getchar");
-                               exit (1);
-                       }
-                       input_head = 0;
-                       break;
-               }
-       }
-       c = input_queue[input_head];
-       input_head = (input_head + 1) % QUEUE_LEN;
-       uc = c;
-       if (sirf_in_len || uc == 0xa0) {
-               if (sirf_in_len < 4096)
-                       sirf_in_message[sirf_in_len++] = uc;
-               if (uc == 0xb3) {
-                       check_sirf_message("recv", sirf_in_message, sirf_in_len);
-                       sirf_in_len = 0;
-               }
-       }
-       return c;
-}
-
-
-void
-ao_serial_putchar(char c)
-{
-       int     i;
-       uint8_t uc = (uint8_t) c;
-
-       if (sirf_message_len || uc == 0xa0) {
-               if (sirf_message_len < 4096)
-                       sirf_message[sirf_message_len++] = uc;
-               if (uc == 0xb3) {
-                       check_sirf_message("send", sirf_message, sirf_message_len);
-                       sirf_message_len = 0;
-               }
-       }
-       for (;;) {
-               i = write(ao_gps_fd, &c, 1);
-               if (i == 1) {
-                       if ((uint8_t) c == 0xb3 || c == '\r') {
-                               static const struct timespec delay = {
-                                       .tv_sec = 0,
-                                       .tv_nsec = 100 * 1000 * 1000
-                               };
-                               tcdrain(ao_gps_fd);
-//                             nanosleep(&delay, NULL);
-                       }
-                       break;
-               }
-               if (i < 0 && (errno == EINTR || errno == EAGAIN))
-                       continue;
-               perror("putchar");
-               exit(1);
-       }
-}
-
-#define AO_SERIAL_SPEED_4800   0
-#define AO_SERIAL_SPEED_57600  1
-
-static void
-ao_serial_set_speed(uint8_t speed)
-{
-       int     fd = ao_gps_fd;
-       struct termios  termios;
-
-       tcdrain(fd);
-       tcgetattr(fd, &termios);
-       switch (speed) {
-       case AO_SERIAL_SPEED_4800:
-               cfsetspeed(&termios, B4800);
-               break;
-       case AO_SERIAL_SPEED_57600:
-               cfsetspeed(&termios, B57600);
-               break;
-       }
-       tcsetattr(fd, TCSAFLUSH, &termios);
-       tcflush(fd, TCIFLUSH);
-}
-
-#include "ao_gps_print.c"
-#include "ao_gps.c"
-
-void
-ao_dump_state(void)
-{
-       double  lat, lon;
-       printf ("%02d:%02d:%02d",
-               ao_gps_data.hour, ao_gps_data.minute,
-               ao_gps_data.second);
-       printf (" nsat %d %svalid",
-               ao_gps_data.flags & AO_GPS_NUM_SAT_MASK,
-               ao_gps_data.flags & AO_GPS_VALID ? "" : "not ");
-       printf (" lat %g lon %g alt %d",
-               ao_gps_data.latitude / 1.0e7,
-               ao_gps_data.longitude / 1.0e7,
-               ao_gps_data.altitude);
-       printf (" speed %g climb %g course %d",
-               ao_gps_data.ground_speed / 100.0,
-               ao_gps_data.climb_rate / 100.0,
-               ao_gps_data.course * 2);
-       printf (" hdop %g h_error %d v_error %d",
-               ao_gps_data.hdop / 5.0,
-               ao_gps_data.h_error, ao_gps_data.v_error);
-       printf("\n");
-}
-
-int
-ao_gps_open(const char *tty)
-{
-       struct termios  termios;
-       int fd;
-
-       fd = open (tty, O_RDWR);
-       if (fd < 0)
-               return -1;
-
-       tcgetattr(fd, &termios);
-       cfmakeraw(&termios);
-       cfsetspeed(&termios, B4800);
-       tcsetattr(fd, TCSAFLUSH, &termios);
-
-       tcdrain(fd);
-       tcflush(fd, TCIFLUSH);
-       return fd;
-}
-
-int
-main (int argc, char **argv)
-{
-       char    *gps_file = "/dev/ttyUSB0";
-
-       ao_gps_fd = ao_gps_open(gps_file);
-       if (ao_gps_fd < 0) {
-               perror (gps_file);
-               exit (1);
-       }
-       ao_gps_setup();
-       ao_gps();
-}
diff --git a/src/ao_host.h b/src/ao_host.h
deleted file mode 100644 (file)
index 38ff84a..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#define _GNU_SOURCE
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define AO_ADC_RING    64
-#define ao_adc_ring_next(n)    (((n) + 1) & (AO_ADC_RING - 1))
-#define ao_adc_ring_prev(n)    (((n) - 1) & (AO_ADC_RING - 1))
-
-/*
- * One set of samples read from the A/D converter
- */
-struct ao_adc {
-       uint16_t        tick;           /* tick when the sample was read */
-       int16_t         accel;          /* accelerometer */
-       int16_t         pres;           /* pressure sensor */
-       int16_t         temp;           /* temperature sensor */
-       int16_t         v_batt;         /* battery voltage */
-       int16_t         sense_d;        /* drogue continuity sense */
-       int16_t         sense_m;        /* main continuity sense */
-};
-
-#define __pdata
-#define __data
-#define __xdata
-#define __code
-#define __reentrant
-
-enum ao_flight_state {
-       ao_flight_startup = 0,
-       ao_flight_idle = 1,
-       ao_flight_pad = 2,
-       ao_flight_boost = 3,
-       ao_flight_fast = 4,
-       ao_flight_coast = 5,
-       ao_flight_drogue = 6,
-       ao_flight_main = 7,
-       ao_flight_landed = 8,
-       ao_flight_invalid = 9
-};
-
-struct ao_adc ao_adc_ring[AO_ADC_RING];
-uint8_t ao_adc_head;
-
-#define ao_led_on(l)
-#define ao_led_off(l)
-#define ao_timer_set_adc_interval(i)
-#define ao_wakeup(wchan) ao_dump_state()
-#define ao_cmd_register(c)
-#define ao_usb_disable()
-#define ao_telemetry_set_interval(x)
-
-enum ao_igniter {
-       ao_igniter_drogue = 0,
-       ao_igniter_main = 1
-};
-
-void
-ao_ignite(enum ao_igniter igniter)
-{
-       printf ("ignite %s\n", igniter == ao_igniter_drogue ? "drogue" : "main");
-}
-
-struct ao_task {
-       int dummy;
-};
-
-#define ao_add_task(t,f,n)
-
-#define ao_log_start()
-#define ao_log_stop()
-
-#define AO_MS_TO_TICKS(ms)     ((ms) / 10)
-#define AO_SEC_TO_TICKS(s)     ((s) * 100)
-
-#define AO_FLIGHT_TEST
-
-struct ao_adc ao_adc_static;
-
-FILE *emulator_in;
-
-void
-ao_dump_state(void);
-
-void
-ao_sleep(void *wchan);
-
-const char const * const ao_state_names[] = {
-       "startup", "idle", "pad", "boost", "fast",
-       "coast", "drogue", "main", "landed", "invalid"
-};
-
-struct ao_cmds {
-       char            cmd;
-       void            (*func)(void);
-       const char      *help;
-};
-
-
-static int16_t altitude_table[2048] = {
-#include "altitude.h"
-};
-
-int16_t
-ao_pres_to_altitude(int16_t pres) __reentrant
-{
-       pres = pres >> 4;
-       if (pres < 0) pres = 0;
-       if (pres > 2047) pres = 2047;
-       return altitude_table[pres];
-}
-
-int16_t
-ao_altitude_to_pres(int16_t alt) __reentrant
-{
-       int16_t pres;
-
-       for (pres = 0; pres < 2047; pres++)
-               if (altitude_table[pres] <= alt)
-                       break;
-       return pres << 4;
-}
-
-struct ao_config {
-       uint16_t        main_deploy;
-       int16_t         accel_zero_g;
-};
-
-#define ao_config_get()
-
-struct ao_config ao_config = { 250, 16000 };
diff --git a/src/ao_ignite.c b/src/ao_ignite.c
deleted file mode 100644 (file)
index be29152..0000000
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-#define AO_IGNITER_DROGUE      P2_3
-#define AO_IGNITER_MAIN                P2_4
-#define AO_IGNITER_DIR         P2DIR
-#define AO_IGNITER_DROGUE_BIT  (1 << 3)
-#define AO_IGNITER_MAIN_BIT    (1 << 4)
-
-/* test these values with real igniters */
-#define AO_IGNITER_OPEN                1000
-#define AO_IGNITER_CLOSED      7000
-#define AO_IGNITER_FIRE_TIME   AO_MS_TO_TICKS(500)
-#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
-
-struct ao_ignition {
-       uint8_t request;
-       uint8_t fired;
-       uint8_t firing;
-};
-
-__xdata struct ao_ignition ao_ignition[2];
-
-void
-ao_ignite(enum ao_igniter igniter) __critical
-{
-       ao_ignition[igniter].request = 1;
-       ao_wakeup(&ao_ignition[0]);
-}
-
-enum ao_igniter_status
-ao_igniter_status(enum ao_igniter igniter)
-{
-       __xdata struct ao_adc adc;
-       __xdata int16_t value;
-       __xdata uint8_t request, firing, fired;
-
-       __critical {
-               ao_adc_sleep();
-               ao_adc_get(&adc);
-               request = ao_ignition[igniter].request;
-               fired = ao_ignition[igniter].fired;
-               firing = ao_ignition[igniter].firing;
-       }
-       if (firing || (request && !fired))
-               return ao_igniter_active;
-
-       value = (AO_IGNITER_CLOSED>>1);
-       switch (igniter) {
-       case ao_igniter_drogue:
-               value = adc.sense_d;
-               break;
-       case ao_igniter_main:
-               value = adc.sense_m;
-               break;
-       }
-       if (value < AO_IGNITER_OPEN)
-               return ao_igniter_open;
-       else if (value > AO_IGNITER_CLOSED)
-               return ao_igniter_ready;
-       else
-               return ao_igniter_unknown;
-}
-
-void
-ao_igniter_fire(enum ao_igniter igniter) __critical
-{
-       ao_ignition[igniter].firing = 1;
-       switch (igniter) {
-       case ao_igniter_drogue:
-               AO_IGNITER_DROGUE = 1;
-               ao_delay(AO_IGNITER_FIRE_TIME);
-               AO_IGNITER_DROGUE = 0;
-               break;
-       case ao_igniter_main:
-               AO_IGNITER_MAIN = 1;
-               ao_delay(AO_IGNITER_FIRE_TIME);
-               AO_IGNITER_MAIN = 0;
-               break;
-       }
-       ao_ignition[igniter].firing = 0;
-}
-
-void
-ao_igniter(void)
-{
-       __xdata enum ao_ignter igniter;
-       __xdata enum ao_igniter_status status;
-
-       for (;;) {
-               ao_sleep(&ao_ignition);
-               for (igniter = ao_igniter_drogue; igniter <= ao_igniter_main; igniter++) {
-                       if (ao_ignition[igniter].request && !ao_ignition[igniter].fired) {
-                               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;
-                       }
-               }
-       }
-}
-
-static uint8_t
-ao_match_word(__code char *word)
-{
-       while (*word) {
-               if (ao_cmd_lex_c != *word) {
-                       ao_cmd_status = ao_cmd_syntax_error;
-                       return 0;
-               }
-               word++;
-               ao_cmd_lex();
-       }
-       return 1;
-}
-
-void
-ao_ignite_manual(void)
-{
-       ao_cmd_white();
-       if (!ao_match_word("DoIt"))
-               return;
-       ao_cmd_white();
-       if (ao_cmd_lex_c == 'm') {
-               if(ao_match_word("main"))
-                       ao_igniter_fire(ao_igniter_main);
-       } else {
-               if(ao_match_word("drogue"))
-                       ao_igniter_fire(ao_igniter_drogue);
-       }
-}
-
-static __code char *igniter_status_names[] = {
-       "unknown", "ready", "active", "open"
-};
-
-void
-ao_ignite_print_status(enum ao_igniter igniter, __code char *name) __reentrant
-{
-       enum ao_igniter_status status = ao_igniter_status(igniter);
-       printf("Igniter: %6s Status: %s\n",
-              name,
-              igniter_status_names[status]);
-}
-
-void
-ao_ignite_test(void)
-{
-       ao_ignite_print_status(ao_igniter_drogue, "drogue");
-       ao_ignite_print_status(ao_igniter_main, "main");
-}
-
-__code struct ao_cmds ao_ignite_cmds[] = {
-       { 'i',  ao_ignite_manual,       "i <key> {main|drogue}              Fire igniter. <key> is doit with D&I" },
-       { 't',  ao_ignite_test,         "t                                  Test igniter continuity" },
-       { 0,    ao_ignite_manual,       NULL },
-};
-
-__xdata struct ao_task ao_igniter_task;
-
-void
-ao_igniter_init(void)
-{
-       AO_IGNITER_DROGUE = 0;
-       AO_IGNITER_MAIN = 0;
-       AO_IGNITER_DIR |= AO_IGNITER_DROGUE_BIT | AO_IGNITER_MAIN_BIT;
-       ao_cmd_register(&ao_ignite_cmds[0]);
-       ao_add_task(&ao_igniter_task, ao_igniter, "igniter");
-}
diff --git a/src/ao_led.c b/src/ao_led.c
deleted file mode 100644 (file)
index 6c698b4..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-#define AO_LED_ALL     (AO_LED_GREEN|AO_LED_RED)
-
-__pdata uint8_t ao_led_enable;
-
-void
-ao_led_on(uint8_t colors)
-{
-       P1 |= (colors & ao_led_enable);
-}
-
-void
-ao_led_off(uint8_t colors)
-{
-       P1 &= ~(colors & ao_led_enable);
-}
-
-void
-ao_led_set(uint8_t colors)
-{
-       P1 = (P1 & ~(ao_led_enable)) | (colors & ao_led_enable);
-}
-
-void
-ao_led_toggle(uint8_t colors)
-{
-       P1 ^= (colors & ao_led_enable);
-}
-
-void
-ao_led_for(uint8_t colors, uint16_t ticks) __reentrant
-{
-       ao_led_on(colors);
-       ao_delay(ticks);
-       ao_led_off(colors);
-}
-
-void
-ao_led_init(uint8_t enable)
-{
-       ao_led_enable = enable;
-       P1SEL &= ~enable;
-       P1 &= ~enable;
-       P1DIR |= enable;
-}
diff --git a/src/ao_log.c b/src/ao_log.c
deleted file mode 100644 (file)
index 19bfdfb..0000000
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-static __pdata uint32_t        ao_log_current_pos;
-static __pdata uint32_t        ao_log_start_pos;
-static __xdata uint8_t ao_log_running;
-static __xdata uint8_t ao_log_mutex;
-
-static uint8_t
-ao_log_csum(uint8_t *b)
-{
-       uint8_t sum = 0x5a;
-       uint8_t i;
-
-       for (i = 0; i < sizeof (struct ao_log_record); i++)
-               sum += *b++;
-       return -sum;
-}
-
-void
-ao_log_data(struct ao_log_record *log)
-{
-       /* set checksum */
-       log->csum = 0;
-       log->csum = ao_log_csum((uint8_t *) log);
-       ao_mutex_get(&ao_log_mutex); {
-               if (ao_log_running) {
-                       ao_ee_write(ao_log_current_pos,
-                                   (uint8_t *) log,
-                                   sizeof (struct ao_log_record));
-                       ao_log_current_pos += sizeof (struct ao_log_record);
-                       if (ao_log_current_pos >= AO_EE_DATA_SIZE)
-                               ao_log_current_pos = 0;
-                       if (ao_log_current_pos == ao_log_start_pos)
-                               ao_log_running = 0;
-               }
-       } ao_mutex_put(&ao_log_mutex);
-}
-
-void
-ao_log_flush(void)
-{
-       ao_ee_flush();
-}
-
-__xdata struct ao_log_record ao_log_dump;
-static __xdata uint16_t ao_log_dump_flight;
-static __xdata uint32_t ao_log_dump_pos;
-
-static uint8_t
-ao_log_dump_check_data(void)
-{
-       if (ao_log_csum((uint8_t *) &ao_log_dump) != 0)
-               return 0;
-       return 1;
-}
-
-static uint8_t
-ao_log_dump_scan(void)
-{
-       if (!ao_ee_read(0, (uint8_t *) &ao_log_dump, sizeof (struct ao_log_record)))
-               ao_panic(AO_PANIC_LOG);
-       if (ao_log_dump_check_data() && ao_log_dump.type == AO_LOG_FLIGHT) {
-               ao_log_dump_flight = ao_log_dump.u.flight.flight;
-               return 1;
-       } else {
-               ao_log_dump_flight = 0;
-               return 0;
-       }
-}
-
-uint8_t
-ao_log_dump_first(void)
-{
-       ao_log_dump_pos = 0;
-       if (!ao_log_dump_scan())
-               return 0;
-       return 1;
-}
-
-uint8_t
-ao_log_dump_next(void)
-{
-       ao_log_dump_pos += sizeof (struct ao_log_record);
-       if (ao_log_dump_pos >= AO_EE_DEVICE_SIZE)
-               return 0;
-       if (!ao_ee_read(ao_log_dump_pos, (uint8_t *) &ao_log_dump,
-                       sizeof (struct ao_log_record)))
-               return 0;
-       return ao_log_dump_check_data();
-}
-
-__xdata uint8_t        ao_log_adc_pos;
-__xdata enum flight_state ao_log_state;
-
-void
-ao_log(void)
-{
-       static __xdata struct ao_log_record     log;
-
-       ao_log_dump_scan();
-
-       while (!ao_log_running)
-               ao_sleep(&ao_log_running);
-
-       log.type = AO_LOG_FLIGHT;
-       log.tick = ao_flight_tick;
-       log.u.flight.ground_accel = ao_ground_accel;
-       log.u.flight.flight = ao_log_dump_flight + 1;
-       ao_log_data(&log);
-
-       /* Write the whole contents of the ring to the log
-        * when starting up.
-        */
-       ao_log_adc_pos = ao_adc_ring_next(ao_adc_head);
-       for (;;) {
-               /* Write samples to EEPROM */
-               while (ao_log_adc_pos != ao_adc_head) {
-                       log.type = AO_LOG_SENSOR;
-                       log.tick = ao_adc_ring[ao_log_adc_pos].tick;
-                       log.u.sensor.accel = ao_adc_ring[ao_log_adc_pos].accel;
-                       log.u.sensor.pres = ao_adc_ring[ao_log_adc_pos].pres;
-                       ao_log_data(&log);
-                       if ((ao_log_adc_pos & 0x1f) == 0) {
-                               log.type = AO_LOG_TEMP_VOLT;
-                               log.tick = ao_adc_ring[ao_log_adc_pos].tick;
-                               log.u.temp_volt.temp = ao_adc_ring[ao_log_adc_pos].temp;
-                               log.u.temp_volt.v_batt = ao_adc_ring[ao_log_adc_pos].v_batt;
-                               ao_log_data(&log);
-                               log.type = AO_LOG_DEPLOY;
-                               log.tick = ao_adc_ring[ao_log_adc_pos].tick;
-                               log.u.deploy.drogue = ao_adc_ring[ao_log_adc_pos].sense_d;
-                               log.u.deploy.main = ao_adc_ring[ao_log_adc_pos].sense_m;
-                               ao_log_data(&log);
-                       }
-                       ao_log_adc_pos = ao_adc_ring_next(ao_log_adc_pos);
-               }
-               /* Write state change to EEPROM */
-               if (ao_flight_state != ao_log_state) {
-                       ao_log_state = ao_flight_state;
-                       log.type = AO_LOG_STATE;
-                       log.tick = ao_flight_tick;
-                       log.u.state.state = ao_log_state;
-                       log.u.state.reason = 0;
-                       ao_log_data(&log);
-
-                       if (ao_log_state == ao_flight_landed)
-                               ao_log_stop();
-               }
-
-               /* Wait for a while */
-               ao_delay(AO_MS_TO_TICKS(100));
-
-               /* Stop logging when told to */
-               while (!ao_log_running)
-                       ao_sleep(&ao_log_running);
-       }
-}
-
-void
-ao_log_start(void)
-{
-       /* start logging */
-       ao_log_running = 1;
-       ao_wakeup(&ao_log_running);
-}
-
-void
-ao_log_stop(void)
-{
-       ao_log_running = 0;
-       ao_log_flush();
-}
-
-static void
-dump_log(void)
-{
-       uint8_t more;
-
-       for (more = ao_log_dump_first(); more; more = ao_log_dump_next()) {
-               printf("%c %4x %4x %4x\n",
-                      ao_log_dump.type,
-                      ao_log_dump.tick,
-                      ao_log_dump.u.anon.d0,
-                      ao_log_dump.u.anon.d1);
-               if (ao_log_dump.type == AO_LOG_STATE &&
-                   ao_log_dump.u.state.state == ao_flight_landed)
-                       break;
-       }
-       printf("end\n");
-}
-
-__code struct ao_cmds ao_log_cmds[] = {
-       { 'l',  dump_log,               "l                                  Dump last flight log" },
-       { 0, dump_log, NULL },
-};
-
-static __xdata struct ao_task ao_log_task;
-
-void
-ao_log_init(void)
-{
-       ao_log_running = 0;
-
-       /* For now, just log the flight starting at the begining of eeprom */
-       ao_log_start_pos = 0;
-       ao_log_current_pos = ao_log_start_pos;
-       ao_log_state = ao_flight_invalid;
-
-       /* Create a task to log events to eeprom */
-       ao_add_task(&ao_log_task, ao_log, "log");
-       ao_cmd_register(&ao_log_cmds[0]);
-}
diff --git a/src/ao_main.c b/src/ao_main.c
deleted file mode 100644 (file)
index 25acccf..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-void
-main(void)
-{
-       ao_clock_init();
-
-       /* Turn on the red LED until the system is stable */
-       ao_led_init();
-       ao_led_on(AO_LED_RED);
-
-       ao_timer_init();
-       ao_adc_init();
-       ao_beep_init();
-       ao_cmd_init();
-       ao_ee_init();
-       ao_flight_init();
-       ao_log_init();
-       ao_report_init();
-       ao_usb_init();
-       ao_serial_init();
-       ao_gps_init();
-       ao_telemetry_init();
-       ao_radio_init();
-       ao_start_scheduler();
-}
diff --git a/src/ao_monitor.c b/src/ao_monitor.c
deleted file mode 100644 (file)
index 5997d42..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-__xdata uint8_t ao_monitoring;
-__pdata uint8_t ao_monitor_led;
-
-void
-ao_monitor(void)
-{
-       __xdata struct ao_radio_recv recv;
-       __xdata char callsign[AO_MAX_CALLSIGN+1];
-       uint8_t state;
-
-       for (;;) {
-               __critical while (!ao_monitoring)
-                       ao_sleep(&ao_monitoring);
-               ao_radio_recv(&recv);
-               state = recv.telemetry.flight_state;
-               memcpy(callsign, recv.telemetry.callsign, AO_MAX_CALLSIGN);
-               if (state > ao_flight_invalid)
-                       state = ao_flight_invalid;
-               if (recv.status & PKT_APPEND_STATUS_1_CRC_OK) {
-                       printf ("CALL %s SERIAL %3d RSSI %4d STATUS %02x STATE %7s ",
-                               callsign,
-                               recv.telemetry.addr,
-                               (int) recv.rssi - 74, recv.status,
-                               ao_state_names[state]);
-                       printf("%5u a: %5d p: %5d t: %5d v: %5d d: %5d m: %5d fa: %5d ga: %d fv: %7ld fp: %5d gp: %5d ",
-                              recv.telemetry.adc.tick,
-                              recv.telemetry.adc.accel,
-                              recv.telemetry.adc.pres,
-                              recv.telemetry.adc.temp,
-                              recv.telemetry.adc.v_batt,
-                              recv.telemetry.adc.sense_d,
-                              recv.telemetry.adc.sense_m,
-                              recv.telemetry.flight_accel,
-                              recv.telemetry.ground_accel,
-                              recv.telemetry.flight_vel,
-                              recv.telemetry.flight_pres,
-                              recv.telemetry.ground_pres);
-                       ao_gps_print(&recv.telemetry.gps);
-                       ao_rssi_set((int) recv.rssi - 74);
-               } else {
-                       printf("CRC INVALID RSSI %3d\n", (int) recv.rssi - 74);
-               }
-               ao_usb_flush();
-               ao_led_toggle(ao_monitor_led);
-       }
-}
-
-__xdata struct ao_task ao_monitor_task;
-
-void
-ao_set_monitor(uint8_t monitoring)
-{
-       ao_monitoring = monitoring;
-       ao_wakeup(&ao_monitoring);
-}
-
-static void
-set_monitor(void)
-{
-       ao_cmd_hex();
-       ao_set_monitor(ao_cmd_lex_i != 0);
-}
-
-__code struct ao_cmds ao_monitor_cmds[] = {
-       { 'm',  set_monitor,    "m <0 off, 1 on>                    Enable/disable radio monitoring" },
-       { 0,    set_monitor,    NULL },
-};
-
-void
-ao_monitor_init(uint8_t monitor_led, uint8_t monitoring) __reentrant
-{
-       ao_monitor_led = monitor_led;
-       ao_monitoring = monitoring;
-       ao_cmd_register(&ao_monitor_cmds[0]);
-       ao_add_task(&ao_monitor_task, ao_monitor, "monitor");
-}
diff --git a/src/ao_mutex.c b/src/ao_mutex.c
deleted file mode 100644 (file)
index c82a7d5..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-void
-ao_mutex_get(__xdata uint8_t *mutex) __reentrant
-{
-       if (*mutex == ao_cur_task->task_id)
-               ao_panic(AO_PANIC_MUTEX);
-       __critical {
-               while (*mutex)
-                       ao_sleep(mutex);
-               *mutex = ao_cur_task->task_id;
-       }
-}
-
-void
-ao_mutex_put(__xdata uint8_t *mutex) __reentrant
-{
-       if (*mutex != ao_cur_task->task_id)
-               ao_panic(AO_PANIC_MUTEX);
-       __critical {
-               *mutex = 0;
-               ao_wakeup(mutex);
-       }
-}
diff --git a/src/ao_panic.c b/src/ao_panic.c
deleted file mode 100644 (file)
index e996371..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-static void
-ao_panic_delay(uint8_t n)
-{
-       uint8_t i = 0, j = 0;
-
-       while (n--)
-               while (--j)
-                       while (--i)
-                               _asm nop _endasm;
-}
-
-void
-ao_panic(uint8_t reason)
-{
-       uint8_t n;
-
-       __critical for (;;) {
-               ao_panic_delay(20);
-               for (n = 0; n < 5; n++) {
-                       ao_led_on(AO_LED_RED);
-                       ao_beep(AO_BEEP_HIGH);
-                       ao_panic_delay(1);
-                       ao_led_off(AO_LED_RED);
-                       ao_beep(AO_BEEP_LOW);
-                       ao_panic_delay(1);
-               }
-               ao_beep(AO_BEEP_OFF);
-               ao_panic_delay(2);
-#pragma disable_warning 126
-               for (n = 0; n < reason; n++) {
-                       ao_led_on(AO_LED_RED);
-                       ao_beep(AO_BEEP_MID);
-                       ao_panic_delay(10);
-                       ao_led_off(AO_LED_RED);
-                       ao_beep(AO_BEEP_OFF);
-                       ao_panic_delay(10);
-               }
-       }
-}
diff --git a/src/ao_product.c b/src/ao_product.c
deleted file mode 100644 (file)
index b42e62c..0000000
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-#include "ao_usb.h"
-#include PRODUCT_DEFS
-
-/* Defines which mark this particular AltOS product */
-
-const uint16_t ao_serial_number = AO_iSerial_NUMBER;
-const char ao_version[] = AO_iVersion_STRING;
-const char ao_manufacturer[] = AO_iManufacturer_STRING;
-const char ao_product[] = AO_iProduct_STRING;
-
-#define LE_WORD(x)    ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8))
-
-/* USB descriptors in one giant block of bytes */
-const uint8_t ao_usb_descriptors [] =
-{
-       /* Device descriptor */
-       0x12,
-       AO_USB_DESC_DEVICE,
-       LE_WORD(0x0110),        /*  bcdUSB */
-       0x02,                   /*  bDeviceClass */
-       0x00,                   /*  bDeviceSubClass */
-       0x00,                   /*  bDeviceProtocol */
-       AO_USB_CONTROL_SIZE,    /*  bMaxPacketSize */
-       LE_WORD(0xFFFE),        /*  idVendor */
-       LE_WORD(0x000A),        /*  idProduct */
-       LE_WORD(0x0100),        /*  bcdDevice */
-       0x01,                   /*  iManufacturer */
-       0x02,                   /*  iProduct */
-       0x03,                   /*  iSerialNumber */
-       0x01,                   /*  bNumConfigurations */
-
-       /* Configuration descriptor */
-       0x09,
-       AO_USB_DESC_CONFIGURATION,
-       LE_WORD(67),            /*  wTotalLength */
-       0x02,                   /*  bNumInterfaces */
-       0x01,                   /*  bConfigurationValue */
-       0x00,                   /*  iConfiguration */
-       0xC0,                   /*  bmAttributes */
-       0x32,                   /*  bMaxPower */
-
-       /* Control class interface */
-       0x09,
-       AO_USB_DESC_INTERFACE,
-       0x00,                   /*  bInterfaceNumber */
-       0x00,                   /*  bAlternateSetting */
-       0x01,                   /*  bNumEndPoints */
-       0x02,                   /*  bInterfaceClass */
-       0x02,                   /*  bInterfaceSubClass */
-       0x01,                   /*  bInterfaceProtocol, linux requires value of 1 for the cdc_acm module */
-       0x00,                   /*  iInterface */
-
-       /* Header functional descriptor */
-       0x05,
-       CS_INTERFACE,
-       0x00,                   /*  bDescriptor SubType Header */
-       LE_WORD(0x0110),        /*  CDC version 1.1 */
-
-       /* Call management functional descriptor */
-       0x05,
-       CS_INTERFACE,
-       0x01,                   /* bDescriptor SubType Call Management */
-       0x01,                   /* bmCapabilities = device handles call management */
-       0x01,                   /* bDataInterface call management interface number */
-
-       /* ACM functional descriptor */
-       0x04,
-       CS_INTERFACE,
-       0x02,                   /* bDescriptor SubType Abstract Control Management */
-       0x02,                   /* bmCapabilities = D1 (Set_line_Coding, Set_Control_Line_State, Get_Line_Coding and Serial_State) */
-
-       /* Union functional descriptor */
-       0x05,
-       CS_INTERFACE,
-       0x06,                   /* bDescriptor SubType Union Functional descriptor */
-       0x00,                   /* bMasterInterface */
-       0x01,                   /* bSlaveInterface0 */
-
-       /* Notification EP */
-       0x07,
-       AO_USB_DESC_ENDPOINT,
-       AO_USB_INT_EP|0x80,     /* bEndpointAddress */
-       0x03,                   /* bmAttributes = intr */
-       LE_WORD(8),             /* wMaxPacketSize */
-       0x0A,                   /* bInterval */
-
-       /* Data class interface descriptor */
-       0x09,
-       AO_USB_DESC_INTERFACE,
-       0x01,                   /* bInterfaceNumber */
-       0x00,                   /* bAlternateSetting */
-       0x02,                   /* bNumEndPoints */
-       0x0A,                   /* bInterfaceClass = data */
-       0x00,                   /* bInterfaceSubClass */
-       0x00,                   /* bInterfaceProtocol */
-       0x00,                   /* iInterface */
-
-       /* Data EP OUT */
-       0x07,
-       AO_USB_DESC_ENDPOINT,
-       AO_USB_OUT_EP,          /* bEndpointAddress */
-       0x02,                   /* bmAttributes = bulk */
-       LE_WORD(AO_USB_OUT_SIZE),/* wMaxPacketSize */
-       0x00,                   /* bInterval */
-
-       /* Data EP in */
-       0x07,
-       AO_USB_DESC_ENDPOINT,
-       AO_USB_IN_EP|0x80,      /* bEndpointAddress */
-       0x02,                   /* bmAttributes = bulk */
-       LE_WORD(AO_USB_IN_SIZE),/* wMaxPacketSize */
-       0x00,                   /* bInterval */
-
-       /* String descriptors */
-       0x04,
-       AO_USB_DESC_STRING,
-       LE_WORD(0x0409),
-
-       /* iManufacturer */
-       AO_iManufacturer_LEN,
-       AO_USB_DESC_STRING,
-       AO_iManufacturer_UCS2,
-
-       /* iProduct */
-       AO_iProduct_LEN,
-       AO_USB_DESC_STRING,
-       AO_iProduct_UCS2,
-
-       /* iSerial */
-       AO_iSerial_LEN,
-       AO_USB_DESC_STRING,
-       AO_iSerial_UCS2,
-
-       /* Terminating zero */
-       0
-};
diff --git a/src/ao_radio.c b/src/ao_radio.c
deleted file mode 100644 (file)
index ca1ec7e..0000000
+++ /dev/null
@@ -1,286 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-/* Values from SmartRF® Studio for:
- *
- * Deviation:  20.507812 kHz
- * Datarate:   38.360596 kBaud
- * Modulation: GFSK
- * RF Freq:    434.549927 MHz
- * Channel:    99.975586 kHz
- * Channel:    0
- * RX filter:  93.75 kHz
- */
-
-/*
- * For 434.550MHz, the frequency value is:
- *
- * 434.550e6 / (24e6 / 2**16) = 1186611.2
- */
-
-#define FREQ_CONTROL   1186611
-
-/*
- * For IF freq of 140.62kHz, the IF value is:
- *
- * 140.62e3 / (24e6 / 2**10) = 6
- */
-
-#define IF_FREQ_CONTROL        6
-
-/*
- * For channel bandwidth of 93.75 kHz, the CHANBW_E and CHANBW_M values are
- *
- * BW = 24e6 / (8 * (4 + M) * 2 ** E)
- *
- * So, M = 0 and E = 3
- */
-
-#define CHANBW_M       0
-#define CHANBW_E       3
-
-/*
- * For a symbol rate of 38360kBaud, the DRATE_E and DRATE_M values are:
- *
- * R = (256 + M) * 2** E * 24e6 / 2**28
- *
- * So M is 163 and E is 10
- */
-
-#define DRATE_E                10
-#define DRATE_M                163
-
-/*
- * For a channel deviation of 20.5kHz, the DEVIATION_E and DEVIATION_M values are:
- *
- * F = 24e6/2**17 * (8 + DEVIATION_M) * 2**DEVIATION_E
- *
- * So M is 6 and E is 3
- */
-
-#define DEVIATION_M    6
-#define DEVIATION_E    3
-
-/* This are from the table for 433MHz */
-
-#define RF_POWER_M30_DBM       0x12
-#define RF_POWER_M20_DBM       0x0e
-#define RF_POWER_M15_DBM       0x1d
-#define RF_POWER_M10_DBM       0x34
-#define RF_POWER_M5_DBM                0x2c
-#define RF_POWER_0_DBM         0x60
-#define RF_POWER_5_DBM         0x84
-#define RF_POWER_7_DBM         0xc8
-#define RF_POWER_10_DBM                0xc0
-
-#define RF_POWER               RF_POWER_10_DBM
-
-static __code uint8_t radio_setup[] = {
-       RF_PA_TABLE7_OFF,       RF_POWER,
-       RF_PA_TABLE6_OFF,       RF_POWER,
-       RF_PA_TABLE5_OFF,       RF_POWER,
-       RF_PA_TABLE4_OFF,       RF_POWER,
-       RF_PA_TABLE3_OFF,       RF_POWER,
-       RF_PA_TABLE2_OFF,       RF_POWER,
-       RF_PA_TABLE1_OFF,       RF_POWER,
-       RF_PA_TABLE0_OFF,       RF_POWER,
-
-       RF_FREQ2_OFF,           (FREQ_CONTROL >> 16) & 0xff,
-       RF_FREQ1_OFF,           (FREQ_CONTROL >> 8) & 0xff,
-       RF_FREQ0_OFF,           (FREQ_CONTROL >> 0) & 0xff,
-
-       RF_FSCTRL1_OFF,         (IF_FREQ_CONTROL << RF_FSCTRL1_FREQ_IF_SHIFT),
-       RF_FSCTRL0_OFF,         (0 << RF_FSCTRL0_FREQOFF_SHIFT),
-
-       RF_MDMCFG4_OFF,         ((CHANBW_E << RF_MDMCFG4_CHANBW_E_SHIFT) |
-                                (CHANBW_M << RF_MDMCFG4_CHANBW_M_SHIFT) |
-                                (DRATE_E << RF_MDMCFG4_DRATE_E_SHIFT)),
-       RF_MDMCFG3_OFF,         (DRATE_M << RF_MDMCFG3_DRATE_M_SHIFT),
-       RF_MDMCFG2_OFF,         (RF_MDMCFG2_DEM_DCFILT_OFF |
-                                RF_MDMCFG2_MOD_FORMAT_GFSK |
-                                RF_MDMCFG2_SYNC_MODE_15_16_THRES),
-       RF_MDMCFG1_OFF,         (RF_MDMCFG1_FEC_EN |
-                                RF_MDMCFG1_NUM_PREAMBLE_4 |
-                                (2 << RF_MDMCFG1_CHANSPC_E_SHIFT)),
-       RF_MDMCFG0_OFF,         (17 << RF_MDMCFG0_CHANSPC_M_SHIFT),
-
-       RF_CHANNR_OFF,          0,
-
-       RF_DEVIATN_OFF,         ((DEVIATION_E << RF_DEVIATN_DEVIATION_E_SHIFT) |
-                                (DEVIATION_M << RF_DEVIATN_DEVIATION_M_SHIFT)),
-
-       /* SmartRF says set LODIV_BUF_CURRENT_TX to 0
-        * And, we're not using power ramping, so use PA_POWER 0
-        */
-       RF_FREND0_OFF,          ((1 << RF_FREND0_LODIV_BUF_CURRENT_TX_SHIFT) |
-                                (0 << RF_FREND0_PA_POWER_SHIFT)),
-
-       RF_FREND1_OFF,          ((1 << RF_FREND1_LNA_CURRENT_SHIFT) |
-                                (1 << RF_FREND1_LNA2MIX_CURRENT_SHIFT) |
-                                (1 << RF_FREND1_LODIV_BUF_CURRENT_RX_SHIFT) |
-                                (2 << RF_FREND1_MIX_CURRENT_SHIFT)),
-
-       RF_FSCAL3_OFF,          0xE9,
-       RF_FSCAL2_OFF,          0x0A,
-       RF_FSCAL1_OFF,          0x00,
-       RF_FSCAL0_OFF,          0x1F,
-
-       RF_TEST2_OFF,           0x88,
-       RF_TEST1_OFF,           0x31,
-       RF_TEST0_OFF,           0x09,
-
-       /* default sync values */
-       RF_SYNC1_OFF,           0xD3,
-       RF_SYNC0_OFF,           0x91,
-
-       /* max packet length */
-       RF_PKTLEN_OFF,          sizeof (struct ao_telemetry),
-
-       RF_PKTCTRL1_OFF,        ((1 << PKTCTRL1_PQT_SHIFT)|
-                                PKTCTRL1_APPEND_STATUS|
-                                PKTCTRL1_ADR_CHK_NONE),
-       RF_PKTCTRL0_OFF,        (RF_PKTCTRL0_WHITE_DATA|
-                                RF_PKTCTRL0_PKT_FORMAT_NORMAL|
-                                RF_PKTCTRL0_CRC_EN|
-                                RF_PKTCTRL0_LENGTH_CONFIG_FIXED),
-       RF_ADDR_OFF,            0x00,
-       RF_MCSM2_OFF,           (RF_MCSM2_RX_TIME_END_OF_PACKET),
-       RF_MCSM1_OFF,           (RF_MCSM1_CCA_MODE_RSSI_BELOW_UNLESS_RECEIVING|
-                                RF_MCSM1_RXOFF_MODE_IDLE|
-                                RF_MCSM1_TXOFF_MODE_IDLE),
-       RF_MCSM0_OFF,           (RF_MCSM0_FS_AUTOCAL_FROM_IDLE|
-                                RF_MCSM0_MAGIC_3|
-                                RF_MCSM0_CLOSE_IN_RX_0DB),
-       RF_FOCCFG_OFF,          (RF_FOCCFG_FOC_PRE_K_3K,
-                                RF_FOCCFG_FOC_POST_K_PRE_K,
-                                RF_FOCCFG_FOC_LIMIT_BW_OVER_4),
-       RF_BSCFG_OFF,           (RF_BSCFG_BS_PRE_K_2K|
-                                RF_BSCFG_BS_PRE_KP_3KP|
-                                RF_BSCFG_BS_POST_KI_PRE_KI|
-                                RF_BSCFG_BS_POST_KP_PRE_KP|
-                                RF_BSCFG_BS_LIMIT_0),
-       RF_AGCCTRL2_OFF,        0x43,
-       RF_AGCCTRL1_OFF,        0x40,
-       RF_AGCCTRL0_OFF,        0x91,
-
-       RF_IOCFG2_OFF,          0x00,
-       RF_IOCFG1_OFF,          0x00,
-       RF_IOCFG0_OFF,          0x00,
-};
-
-
-static __code uint8_t telemetry_setup[] = {
-       RF_MDMCFG4_OFF,         ((CHANBW_E << RF_MDMCFG4_CHANBW_E_SHIFT) |
-                                (CHANBW_M << RF_MDMCFG4_CHANBW_M_SHIFT) |
-                                (DRATE_E << RF_MDMCFG4_DRATE_E_SHIFT)),
-       RF_MDMCFG3_OFF,         (DRATE_M << RF_MDMCFG3_DRATE_M_SHIFT),
-       RF_MDMCFG2_OFF,         (RF_MDMCFG2_DEM_DCFILT_OFF |
-                                RF_MDMCFG2_MOD_FORMAT_GFSK |
-                                RF_MDMCFG2_SYNC_MODE_15_16_THRES),
-       RF_MDMCFG1_OFF,         (RF_MDMCFG1_FEC_EN |
-                                RF_MDMCFG1_NUM_PREAMBLE_4 |
-                                (2 << RF_MDMCFG1_CHANSPC_E_SHIFT)),
-
-       RF_DEVIATN_OFF,         ((DEVIATION_E << RF_DEVIATN_DEVIATION_E_SHIFT) |
-                                (DEVIATION_M << RF_DEVIATN_DEVIATION_M_SHIFT)),
-
-       /* max packet length */
-       RF_PKTLEN_OFF,          sizeof (struct ao_telemetry),
-       RF_PKTCTRL1_OFF,        ((1 << PKTCTRL1_PQT_SHIFT)|
-                                PKTCTRL1_APPEND_STATUS|
-                                PKTCTRL1_ADR_CHK_NONE),
-       RF_PKTCTRL0_OFF,        (RF_PKTCTRL0_WHITE_DATA|
-                                RF_PKTCTRL0_PKT_FORMAT_NORMAL|
-                                RF_PKTCTRL0_CRC_EN|
-                                RF_PKTCTRL0_LENGTH_CONFIG_FIXED),
-};
-
-__xdata uint8_t        ao_radio_dma;
-__xdata uint8_t ao_radio_dma_done;
-__xdata uint8_t ao_radio_mutex;
-
-static void
-ao_radio_idle(void)
-{
-       if (RF_MARCSTATE != RF_MARCSTATE_IDLE)
-       {
-               RFST = RFST_SIDLE;
-               do {
-                       ao_yield();
-               } while (RF_MARCSTATE != RF_MARCSTATE_IDLE);
-       }
-}
-
-void
-ao_radio_send(__xdata struct ao_telemetry *telemetry) __reentrant
-{
-       ao_config_get();
-       ao_mutex_get(&ao_radio_mutex);
-       ao_radio_idle();
-       RF_CHANNR = ao_config.radio_channel;
-       ao_dma_set_transfer(ao_radio_dma,
-                           telemetry,
-                           &RFDXADDR,
-                           sizeof (struct ao_telemetry),
-                           DMA_CFG0_WORDSIZE_8 |
-                           DMA_CFG0_TMODE_SINGLE |
-                           DMA_CFG0_TRIGGER_RADIO,
-                           DMA_CFG1_SRCINC_1 |
-                           DMA_CFG1_DESTINC_0 |
-                           DMA_CFG1_PRIORITY_HIGH);
-       ao_dma_start(ao_radio_dma);
-       RFST = RFST_STX;
-       __critical while (!ao_radio_dma_done)
-               ao_sleep(&ao_radio_dma_done);
-       ao_mutex_put(&ao_radio_mutex);
-}
-
-void
-ao_radio_recv(__xdata struct ao_radio_recv *radio) __reentrant
-{
-       ao_config_get();
-       ao_mutex_get(&ao_radio_mutex);
-       ao_radio_idle();
-       RF_CHANNR = ao_config.radio_channel;
-       ao_dma_set_transfer(ao_radio_dma,
-                           &RFDXADDR,
-                           radio,
-                           sizeof (struct ao_radio_recv),
-                           DMA_CFG0_WORDSIZE_8 |
-                           DMA_CFG0_TMODE_SINGLE |
-                           DMA_CFG0_TRIGGER_RADIO,
-                           DMA_CFG1_SRCINC_0 |
-                           DMA_CFG1_DESTINC_1 |
-                           DMA_CFG1_PRIORITY_HIGH);
-       ao_dma_start(ao_radio_dma);
-       RFST = RFST_SRX;
-       __critical while (!ao_radio_dma_done)
-               ao_sleep(&ao_radio_dma_done);
-       ao_mutex_put(&ao_radio_mutex);
-}
-
-void
-ao_radio_init(void)
-{
-       uint8_t i;
-       for (i = 0; i < sizeof (radio_setup); i += 2)
-               RF[radio_setup[i]] = radio_setup[i+1];
-       ao_radio_dma_done = 1;
-       ao_radio_dma = ao_dma_alloc(&ao_radio_dma_done);
-}
diff --git a/src/ao_report.c b/src/ao_report.c
deleted file mode 100644 (file)
index 14eaf42..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-static const char * __xdata flight_reports[] = {
-       "...",          /* startup, 'S' */
-       "..",           /* idle 'I' */
-       ".--.",         /* pad 'P' */
-       "-...",         /* boost 'B' */
-       "..-.",         /* fast 'F' */
-       "-.-.",         /* coast 'C' */
-       "-..",          /* drogue 'D' */
-       "--",           /* main 'M' */
-       ".-..",         /* landed 'L' */
-       ".-.-.-",       /* invalid */
-};
-
-#if 1
-#define signal(time)   ao_beep_for(AO_BEEP_MID, time)
-#else
-#define signal(time)   ao_led_for(AO_LED_RED, time)
-#endif
-#define pause(time)    ao_delay(time)
-
-static __xdata enum ao_flight_state ao_report_state;
-
-static void
-ao_report_beep(void) __reentrant
-{
-       char *r = flight_reports[ao_flight_state];
-       char c;
-
-       if (!r)
-               return;
-       while (c = *r++) {
-               if (c == '.')
-                       signal(AO_MS_TO_TICKS(200));
-               else
-                       signal(AO_MS_TO_TICKS(600));
-               pause(AO_MS_TO_TICKS(200));
-       }
-       pause(AO_MS_TO_TICKS(400));
-}
-
-static void
-ao_report_digit(uint8_t digit) __reentrant
-{
-       if (!digit) {
-               signal(AO_MS_TO_TICKS(500));
-               pause(AO_MS_TO_TICKS(200));
-       } else {
-               while (digit--) {
-                       signal(AO_MS_TO_TICKS(200));
-                       pause(AO_MS_TO_TICKS(200));
-               }
-       }
-       pause(AO_MS_TO_TICKS(300));
-}
-
-static void
-ao_report_altitude(void)
-{
-       __xdata int16_t agl = ao_pres_to_altitude(ao_min_pres) - ao_pres_to_altitude(ao_ground_pres);
-       __xdata uint8_t digits[10];
-       __xdata uint8_t ndigits, i;
-
-       if (agl < 0)
-               agl = 0;
-       ndigits = 0;
-       do {
-               digits[ndigits++] = agl % 10;
-               agl /= 10;
-       } while (agl);
-
-       for (;;) {
-               ao_report_beep();
-               i = ndigits;
-               do
-                       ao_report_digit(digits[--i]);
-               while (i != 0);
-               pause(AO_SEC_TO_TICKS(5));
-       }
-}
-
-void
-ao_report(void)
-{
-       ao_report_state = ao_flight_state;
-       for(;;) {
-               if (ao_flight_state == ao_flight_landed)
-                       ao_report_altitude();
-               ao_report_beep();
-               __critical {
-                       while (ao_report_state == ao_flight_state)
-                               ao_sleep(DATA_TO_XDATA(&ao_flight_state));
-                       ao_report_state = ao_flight_state;
-               }
-       }
-}
-
-static __xdata struct ao_task ao_report_task;
-
-void
-ao_report_init(void)
-{
-       ao_add_task(&ao_report_task, ao_report, "report");
-}
diff --git a/src/ao_rssi.c b/src/ao_rssi.c
deleted file mode 100644 (file)
index 6912b9a..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-static __xdata volatile uint16_t       ao_rssi_time;
-static __xdata volatile uint16_t       ao_rssi_delay;
-static __xdata uint8_t                 ao_rssi_led;
-
-void
-ao_rssi(void)
-{
-       for (;;) {
-               while ((int16_t) (ao_time() - ao_rssi_time) > AO_SEC_TO_TICKS(3))
-                       ao_sleep(&ao_rssi_time);
-               ao_led_for(ao_rssi_led, AO_MS_TO_TICKS(100));
-               ao_delay(ao_rssi_delay);
-       }
-}
-
-void
-ao_rssi_set(int rssi_value)
-{
-       if (rssi_value > 0)
-               rssi_value = 0;
-       ao_rssi_delay = AO_MS_TO_TICKS((-rssi_value) * 5);
-       ao_rssi_time = ao_time();
-       ao_wakeup(&ao_rssi_time);
-}
-
-__xdata struct ao_task ao_rssi_task;
-
-void
-ao_rssi_init(uint8_t rssi_led)
-{
-       ao_rssi_led = rssi_led;
-       ao_rssi_delay = 0;
-       ao_add_task(&ao_rssi_task, ao_rssi, "rssi");
-}
diff --git a/src/ao_serial.c b/src/ao_serial.c
deleted file mode 100644 (file)
index 5911035..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-volatile __xdata struct ao_fifo        ao_usart1_rx_fifo;
-volatile __xdata struct ao_fifo        ao_usart1_tx_fifo;
-
-void
-ao_serial_rx1_isr(void) interrupt 3
-{
-       if (!ao_fifo_full(ao_usart1_rx_fifo))
-               ao_fifo_insert(ao_usart1_rx_fifo, U1DBUF);
-       ao_wakeup(&ao_usart1_rx_fifo);
-}
-
-static __xdata uint8_t ao_serial_tx1_started;
-
-static void
-ao_serial_tx1_start(void)
-{
-       if (!ao_fifo_empty(ao_usart1_tx_fifo) &&
-           !ao_serial_tx1_started)
-       {
-               ao_serial_tx1_started = 1;
-               ao_fifo_remove(ao_usart1_tx_fifo, U1DBUF);
-       }
-}
-
-void
-ao_serial_tx1_isr(void) interrupt 14
-{
-       UTX1IF = 0;
-       ao_serial_tx1_started = 0;
-       ao_serial_tx1_start();
-       ao_wakeup(&ao_usart1_tx_fifo);
-}
-
-static __pdata serial_echo;
-
-char
-ao_serial_getchar(void) __critical
-{
-       char    c;
-       while (ao_fifo_empty(ao_usart1_rx_fifo))
-               ao_sleep(&ao_usart1_rx_fifo);
-       ao_fifo_remove(ao_usart1_rx_fifo, c);
-       if (serial_echo) {
-               printf("%02x\n", ((int) c) & 0xff);
-               flush();
-       }
-       return c;
-}
-
-void
-ao_serial_putchar(char c) __critical
-{
-       while (ao_fifo_full(ao_usart1_tx_fifo))
-               ao_sleep(&ao_usart1_tx_fifo);
-       ao_fifo_insert(ao_usart1_tx_fifo, c);
-       ao_serial_tx1_start();
-}
-
-static void
-ao_serial_drain(void) __critical
-{
-       while (!ao_fifo_empty(ao_usart1_tx_fifo))
-               ao_sleep(&ao_usart1_tx_fifo);
-}
-
-static void
-send_serial(void)
-{
-       ao_cmd_white();
-       while (ao_cmd_lex_c != '\n') {
-               ao_serial_putchar(ao_cmd_lex_c);
-               ao_cmd_lex();
-       }
-}
-
-static void
-monitor_serial(void)
-{
-       ao_cmd_hex();
-       serial_echo = ao_cmd_lex_i != 0;
-}
-
-static void
-serial_baud(void)
-{
-       ao_cmd_hex();
-       ao_serial_set_speed(ao_cmd_lex_i);
-}
-
-__code struct ao_cmds ao_serial_cmds[] = {
-       { 'S', send_serial,             "S <data>                           Send data to serial line" },
-       { 'M', monitor_serial,          "M <enable>                         Monitor serial data" },
-       { 'B', serial_baud,             "B <0 = 4800, 1 = 57600>            Set serial baud rate" },
-       { 0, send_serial, NULL },
-};
-
-static const struct {
-       uint8_t baud;
-       uint8_t gcr;
-} ao_serial_speeds[] = {
-       /* [AO_SERIAL_SPEED_4800] = */ {
-               /* .baud = */ 163,
-               /* .gcr  = */ (7 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
-       },
-       /* [AO_SERIAL_SPEED_57600] = */ {
-               /* .baud = */ 59,
-               /* .gcr =  */ (11 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
-       },
-};
-
-void
-ao_serial_set_speed(uint8_t speed)
-{
-       ao_serial_drain();
-       if (speed > AO_SERIAL_SPEED_57600)
-               return;
-       U1BAUD = ao_serial_speeds[speed].baud;
-       U1GCR = ao_serial_speeds[speed].gcr;
-}
-
-void
-ao_serial_init(void)
-{
-       /* Set up the USART pin assignment */
-       PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_2;
-
-       /* ee has already set the P2SEL bits */
-
-       /* Make the USART pins be controlled by the USART */
-       P1SEL |= (1 << 6) | (1 << 7);
-
-       /* UART mode with receiver enabled */
-       U1CSR = (UxCSR_MODE_UART | UxCSR_RE);
-
-       /* Pick a 4800 baud rate */
-       ao_serial_set_speed(AO_SERIAL_SPEED_4800);
-
-       /* Reasonable serial parameters */
-       U1UCR = (UxUCR_FLUSH |
-                UxUCR_FLOW_DISABLE |
-                UxUCR_D9_ODD_PARITY |
-                UxUCR_BIT9_8_BITS |
-                UxUCR_PARITY_DISABLE |
-                UxUCR_SPB_1_STOP_BIT |
-                UxUCR_STOP_HIGH |
-                UxUCR_START_LOW);
-
-       IEN0 |= IEN0_URX1IE;
-       IEN2 |= IEN2_UTX1IE;
-
-       ao_cmd_register(&ao_serial_cmds[0]);
-}
diff --git a/src/ao_state.c b/src/ao_state.c
deleted file mode 100644 (file)
index ed197aa..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-const char const * const ao_state_names[] = {
-       "startup", "idle", "pad", "boost", "fast",
-       "coast", "drogue", "main", "landed", "invalid"
-};
diff --git a/src/ao_stdio.c b/src/ao_stdio.c
deleted file mode 100644 (file)
index fb8ce09..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-/*
- * Basic I/O functions to support SDCC stdio package
- */
-
-void
-putchar(char c)
-{
-       if (c == '\n')
-               ao_usb_putchar('\r');
-       ao_usb_putchar(c);
-}
-
-void
-flush(void)
-{
-       ao_usb_flush();
-}
-
-char
-getchar(void)
-{
-       return ao_usb_getchar();
-}
diff --git a/src/ao_task.c b/src/ao_task.c
deleted file mode 100644 (file)
index 12b7394..0000000
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-#define AO_NO_TASK_INDEX       0xff
-
-__xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS];
-__data uint8_t ao_num_tasks;
-__data uint8_t ao_cur_task_index;
-__xdata struct ao_task *__data ao_cur_task;
-
-void
-ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *name) __reentrant
-{
-       uint8_t __xdata *stack;
-       if (ao_num_tasks == AO_NUM_TASKS)
-               ao_panic(AO_PANIC_NO_TASK);
-       ao_tasks[ao_num_tasks++] = task;
-       task->task_id = ao_num_tasks;
-       task->name = name;
-       /*
-        * Construct a stack frame so that it will 'return'
-        * to the start of the task
-        */
-       stack = task->stack;
-
-       *stack++ = ((uint16_t) start);
-       *stack++ = ((uint16_t) start) >> 8;
-
-       /* and the stuff saved by ao_switch */
-       *stack++ = 0;           /* acc */
-       *stack++ = 0x80;        /* IE */
-       *stack++ = 0;           /* DPL */
-       *stack++ = 0;           /* DPH */
-       *stack++ = 0;           /* B */
-       *stack++ = 0;           /* R2 */
-       *stack++ = 0;           /* R3 */
-       *stack++ = 0;           /* R4 */
-       *stack++ = 0;           /* R5 */
-       *stack++ = 0;           /* R6 */
-       *stack++ = 0;           /* R7 */
-       *stack++ = 0;           /* R0 */
-       *stack++ = 0;           /* R1 */
-       *stack++ = 0;           /* PSW */
-       *stack++ = 0;           /* BP */
-       task->stack_count = stack - task->stack;
-       task->wchan = NULL;
-}
-
-/* Task switching function. This must not use any stack variables */
-void
-ao_yield(void) _naked
-{
-
-       /* Save current context */
-       _asm
-               /* Push ACC first, as when restoring the context it must be restored
-                * last (it is used to set the IE register). */
-               push    ACC
-               /* Store the IE register then enable interrupts. */
-               push    _IEN0
-               setb    _EA
-               push    DPL
-               push    DPH
-               push    b
-               push    ar2
-               push    ar3
-               push    ar4
-               push    ar5
-               push    ar6
-               push    ar7
-               push    ar0
-               push    ar1
-               push    PSW
-       _endasm;
-       PSW = 0;
-       _asm
-               push    _bp
-       _endasm;
-
-       if (ao_cur_task_index != AO_NO_TASK_INDEX)
-       {
-               uint8_t stack_len;
-               __data uint8_t *stack_ptr;
-               __xdata uint8_t *save_ptr;
-               /* Save the current stack */
-               stack_len = SP - (AO_STACK_START - 1);
-               ao_cur_task->stack_count = stack_len;
-               stack_ptr = (uint8_t __data *) AO_STACK_START;
-               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
-               do
-                       *save_ptr++ = *stack_ptr++;
-               while (--stack_len);
-       }
-
-       /* Empty the stack; might as well let interrupts have the whole thing */
-       SP = AO_STACK_START - 1;
-
-       /* Find a task to run. If there isn't any runnable task,
-        * this loop will run forever, which is just fine
-        */
-       {
-               __pdata uint8_t ao_next_task_index = ao_cur_task_index;
-               for (;;) {
-                       ++ao_next_task_index;
-                       if (ao_next_task_index == ao_num_tasks)
-                               ao_next_task_index = 0;
-
-                       ao_cur_task = ao_tasks[ao_next_task_index];
-                       if (ao_cur_task->wchan == NULL) {
-                               ao_cur_task_index = ao_next_task_index;
-                               break;
-                       }
-
-                       /* Enter lower power mode when there isn't anything to do */
-                       if (ao_next_task_index == ao_cur_task_index)
-                               PCON = PCON_IDLE;
-               }
-       }
-
-       {
-               uint8_t stack_len;
-               __data uint8_t *stack_ptr;
-               __xdata uint8_t *save_ptr;
-
-               /* Restore the old stack */
-               stack_len = ao_cur_task->stack_count;
-               SP = AO_STACK_START - 1 + stack_len;
-
-               stack_ptr = (uint8_t __data *) AO_STACK_START;
-               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;
-               do
-                       *stack_ptr++ = *save_ptr++;
-               while (--stack_len);
-       }
-
-       _asm
-               pop             _bp
-               pop             PSW
-               pop             ar1
-               pop             ar0
-               pop             ar7
-               pop             ar6
-               pop             ar5
-               pop             ar4
-               pop             ar3
-               pop             ar2
-               pop             b
-               pop             DPH
-               pop             DPL
-               /* The next byte of the stack is the IE register.  Only the global
-               enable bit forms part of the task context.  Pop off the IE then set
-               the global enable bit to match that of the stored IE register. */
-               pop             ACC
-               JB              ACC.7,0098$
-               CLR             _EA
-               LJMP    0099$
-       0098$:
-               SETB            _EA
-       0099$:
-               /* Finally pop off the ACC, which was the first register saved. */
-               pop             ACC
-               ret
-       _endasm;
-}
-
-void
-ao_sleep(__xdata void *wchan)
-{
-       __critical {
-               ao_cur_task->wchan = wchan;
-       }
-       ao_yield();
-}
-
-void
-ao_wakeup(__xdata void *wchan)
-{
-       uint8_t i;
-
-       for (i = 0; i < ao_num_tasks; i++)
-               if (ao_tasks[i]->wchan == wchan)
-                       ao_tasks[i]->wchan = NULL;
-}
-
-void
-ao_task_info(void)
-{
-       uint8_t i;
-       uint8_t pc_loc;
-       __xdata struct ao_task *task;
-
-       for (i = 0; i < ao_num_tasks; i++) {
-               task = ao_tasks[i];
-               pc_loc = task->stack_count - 17;
-               printf("%12s: wchan %04x pc %04x\n",
-                      task->name,
-                      (int16_t) task->wchan,
-                      (task->stack[pc_loc]) | (task->stack[pc_loc+1] << 8));
-       }
-}
-
-void
-ao_start_scheduler(void)
-{
-       ao_cur_task_index = AO_NO_TASK_INDEX;
-       ao_cur_task = NULL;
-       ao_yield();
-}
diff --git a/src/ao_teledongle.c b/src/ao_teledongle.c
deleted file mode 100644 (file)
index 4ebc53a..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#define AO_NO_SERIAL_ISR 1
-#define AO_NO_ADC_ISR 1
-#include "ao.h"
-
-void
-main(void)
-{
-       ao_clock_init();
-
-       /* Turn on the LED until the system is stable */
-       ao_led_init(AO_LED_RED|AO_LED_GREEN);
-       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_dbg_init();
-       ao_config_init();
-       ao_start_scheduler();
-}
diff --git a/src/ao_telemetrum.c b/src/ao_telemetrum.c
deleted file mode 100644 (file)
index 5e951b4..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-void
-main(void)
-{
-       ao_clock_init();
-
-       /* Turn on the red LED until the system is stable */
-       ao_led_init(AO_LED_RED|AO_LED_GREEN);
-       ao_led_on(AO_LED_RED);
-
-       ao_timer_init();
-       ao_adc_init();
-       ao_beep_init();
-       ao_cmd_init();
-       ao_ee_init();
-       ao_flight_init();
-       ao_log_init();
-       ao_report_init();
-       ao_usb_init();
-       ao_serial_init();
-       ao_gps_init();
-       ao_gps_report_init();
-       ao_telemetry_init();
-       ao_radio_init();
-       ao_igniter_init();
-       ao_dbg_init();
-       ao_config_init();
-       ao_start_scheduler();
-}
diff --git a/src/ao_telemetry.c b/src/ao_telemetry.c
deleted file mode 100644 (file)
index 463bcd9..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-__xdata uint16_t ao_telemetry_interval = 0;
-
-void
-ao_telemetry(void)
-{
-       static __xdata struct ao_telemetry telemetry;
-
-       ao_config_get();
-       memcpy(telemetry.callsign, ao_config.callsign, AO_MAX_CALLSIGN);
-       telemetry.addr = ao_serial_number;
-       for (;;) {
-               while (ao_telemetry_interval == 0)
-                       ao_sleep(&ao_telemetry_interval);
-               telemetry.flight_state = ao_flight_state;
-               telemetry.flight_accel = ao_flight_accel;
-               telemetry.ground_accel = ao_ground_accel;
-               telemetry.flight_vel = ao_flight_vel;
-               telemetry.flight_pres = ao_flight_pres;
-               telemetry.ground_pres = ao_ground_pres;
-               ao_adc_get(&telemetry.adc);
-               ao_mutex_get(&ao_gps_mutex);
-               memcpy(&telemetry.gps, &ao_gps_data, sizeof (struct ao_gps_data));
-               ao_mutex_put(&ao_gps_mutex);
-               ao_radio_send(&telemetry);
-               ao_delay(ao_telemetry_interval);
-       }
-}
-
-void
-ao_telemetry_set_interval(uint16_t interval)
-{
-       ao_telemetry_interval = interval;
-       ao_wakeup(&ao_telemetry_interval);
-}
-
-__xdata struct ao_task ao_telemetry_task;
-
-void
-ao_telemetry_init()
-{
-       ao_add_task(&ao_telemetry_task, ao_telemetry, "telemetry");
-}
diff --git a/src/ao_teleterra.c b/src/ao_teleterra.c
deleted file mode 100644 (file)
index 6464ccc..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#define AO_NO_ADC_ISR 1
-#include "ao.h"
-
-void
-main(void)
-{
-       ao_clock_init();
-
-       /* Turn on the red LED until the system is stable */
-       ao_led_init(AO_LED_RED|AO_LED_GREEN);
-       ao_led_on(AO_LED_RED);
-       ao_timer_init();
-       ao_beep_init();
-       ao_cmd_init();
-       ao_usb_init();
-       ao_serial_init();
-       ao_gps_init();
-       ao_monitor_init(AO_LED_GREEN, TRUE);
-       ao_radio_init();
-       ao_dbg_init();
-       ao_config_init();
-       ao_start_scheduler();
-}
diff --git a/src/ao_test.c b/src/ao_test.c
deleted file mode 100644 (file)
index b9f7d33..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-struct ao_task __xdata blink_0_task;
-struct ao_task __xdata blink_1_task;
-struct ao_task __xdata wakeup_task;
-struct ao_task __xdata beep_task;
-struct ao_task __xdata echo_task;
-
-void delay(int n) __reentrant
-{
-       uint8_t j = 0;
-       while (--n)
-               while (--j)
-                       ao_yield();
-}
-
-static __xdata uint8_t blink_chan;
-
-void
-blink_0(void)
-{
-       uint8_t b = 0;
-       for (;;) {
-               b = 1 - b;
-               if (b)
-                       ao_led_on(AO_LED_GREEN);
-               else
-                       ao_led_off(AO_LED_GREEN);
-               ao_sleep(&blink_chan);
-       }
-}
-
-void
-blink_1(void)
-{
-       static __xdata struct ao_adc adc;
-
-       for (;;) {
-               ao_sleep(&ao_adc_ring);
-               ao_adc_get(&adc);
-               if (adc.accel < 15900)
-                       ao_led_on(AO_LED_RED);
-               else
-                       ao_led_off(AO_LED_RED);
-       }
-}
-
-void
-wakeup(void)
-{
-       for (;;) {
-               ao_delay(AO_MS_TO_TICKS(100));
-               ao_wakeup(&blink_chan);
-       }
-}
-
-void
-beep(void)
-{
-       static __xdata struct ao_adc adc;
-
-       for (;;) {
-               ao_delay(AO_SEC_TO_TICKS(1));
-               ao_adc_get(&adc);
-               if (adc.temp > 7400)
-                       ao_beep_for(AO_BEEP_LOW, AO_MS_TO_TICKS(50));
-       }
-}
-
-void
-echo(void)
-{
-       char    c;
-       for (;;) {
-               ao_usb_flush();
-               c = ao_usb_getchar();
-               ao_usb_putchar(c);
-               if (c == '\r')
-                       ao_usb_putchar('\n');
-       }
-}
-
-void
-main(void)
-{
-       ao_clock_init();
-
-//     ao_add_task(&blink_0_task, blink_0);
-//     ao_add_task(&blink_1_task, blink_1);
-//     ao_add_task(&wakeup_task, wakeup);
-//     ao_add_task(&beep_task, beep);
-       ao_add_task(&echo_task, echo);
-       ao_timer_init();
-       ao_adc_init();
-       ao_beep_init();
-       ao_led_init();
-       ao_usb_init();
-
-       ao_start_scheduler();
-}
diff --git a/src/ao_tidongle.c b/src/ao_tidongle.c
deleted file mode 100644 (file)
index 3b7c273..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#define AO_NO_SERIAL_ISR 1
-#define AO_NO_ADC_ISR 1
-#include "ao.h"
-
-void
-main(void)
-{
-       ao_clock_init();
-
-       /* Turn on the LED until the system is stable */
-       ao_led_init(AO_LED_RED);
-       ao_led_on(AO_LED_RED);
-       ao_timer_init();
-       ao_cmd_init();
-       ao_usb_init();
-       ao_monitor_init(AO_LED_RED, TRUE);
-       ao_rssi_init(AO_LED_RED);
-       ao_radio_init();
-       ao_dbg_init();
-       ao_config_init();
-       /* Bring up the USB link */
-       P1DIR |= 1;
-       P1 |= 1;
-       ao_start_scheduler();
-}
diff --git a/src/ao_timer.c b/src/ao_timer.c
deleted file mode 100644 (file)
index 78c6e06..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-
-static volatile __data uint16_t ao_tick_count;
-
-uint16_t ao_time(void) __critical
-{
-       return ao_tick_count;
-}
-
-void
-ao_delay(uint16_t ticks)
-{
-       uint16_t until = ao_time() + ticks;
-
-       while ((int16_t) (until - ao_time()) > 0)
-               ao_sleep(DATA_TO_XDATA(&ao_tick_count));
-}
-
-#define T1_CLOCK_DIVISOR       8       /* 24e6/8 = 3e6 */
-#define T1_SAMPLE_TIME         30000   /* 3e6/30000 = 100 */
-
-volatile __data uint8_t        ao_adc_interval = 1;
-volatile __data uint8_t        ao_adc_count;
-
-void ao_timer_isr(void) interrupt 9
-{
-       ++ao_tick_count;
-       if (++ao_adc_count == ao_adc_interval) {
-               ao_adc_count = 0;
-               ao_adc_poll();
-       }
-       ao_wakeup(DATA_TO_XDATA(&ao_tick_count));
-}
-
-void
-ao_timer_set_adc_interval(uint8_t interval) __critical
-{
-       ao_adc_interval = interval;
-       ao_adc_count = 0;
-}
-
-void
-ao_timer_init(void)
-{
-       /* NOTE:  This uses a timer only present on cc1111 architecture. */
-
-       /* disable timer 1 */
-       T1CTL = 0;
-
-       /* set the sample rate */
-       T1CC0H = T1_SAMPLE_TIME >> 8;
-       T1CC0L = (uint8_t) T1_SAMPLE_TIME;
-
-       T1CCTL0 = T1CCTL_MODE_COMPARE;
-       T1CCTL1 = 0;
-       T1CCTL2 = 0;
-
-       /* clear timer value */
-       T1CNTL = 0;
-
-       /* enable overflow interrupt */
-       OVFIM = 1;
-       /* enable timer 1 interrupt */
-       T1IE = 1;
-
-       /* enable timer 1 in module mode, dividing by 8 */
-       T1CTL = T1CTL_MODE_MODULO | T1CTL_DIV_8;
-}
-
-/*
- * AltOS always cranks the clock to the max frequency
- */
-void
-ao_clock_init(void)
-{
-       /* Switch system clock to crystal oscilator */
-       CLKCON = (CLKCON & ~CLKCON_OSC_MASK) | (CLKCON_OSC_XTAL);
-
-       while (!(SLEEP & SLEEP_XOSC_STB))
-               ;
-
-       /* Crank up the timer tick and system clock speed */
-       CLKCON = ((CLKCON & ~(CLKCON_TICKSPD_MASK | CLKCON_CLKSPD_MASK)) |
-                 (CLKCON_TICKSPD_1 | CLKCON_CLKSPD_1));
-
-       while ((CLKCON & (CLKCON_TICKSPD_MASK|CLKCON_CLKSPD_MASK)) !=
-              (CLKCON_TICKSPD_1 | CLKCON_CLKSPD_1))
-               ;
-}
diff --git a/src/ao_usb.c b/src/ao_usb.c
deleted file mode 100644 (file)
index 99f0715..0000000
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#include "ao.h"
-#include "ao_usb.h"
-
-struct ao_task __xdata ao_usb_task;
-
-static __xdata uint16_t        ao_usb_in_bytes;
-static __xdata uint16_t        ao_usb_out_bytes;
-static __xdata uint8_t ao_usb_iif;
-static __xdata uint8_t ao_usb_running;
-
-static void
-ao_usb_set_interrupts(void)
-{
-       /* IN interrupts on the control an IN endpoints */
-       USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
-
-       /* OUT interrupts on the OUT endpoint */
-       USBOIE = (1 << AO_USB_OUT_EP);
-
-       /* Only care about reset */
-       USBCIE = USBCIE_RSTIE;
-}
-
-/* This interrupt is shared with port 2,
- * so when we hook that up, fix this
- */
-void
-ao_usb_isr(void) interrupt 6
-{
-       USBIF = 0;
-       ao_usb_iif |= USBIIF;
-       if (ao_usb_iif & 1)
-               ao_wakeup(&ao_usb_task);
-       if (ao_usb_iif & (1 << AO_USB_IN_EP))
-               ao_wakeup(&ao_usb_in_bytes);
-
-       if (USBOIF & (1 << AO_USB_OUT_EP))
-               ao_wakeup(&ao_usb_out_bytes);
-
-       if (USBCIF & USBCIF_RSTIF)
-               ao_usb_set_interrupts();
-}
-
-struct ao_usb_setup {
-       uint8_t         dir_type_recip;
-       uint8_t         request;
-       uint16_t        value;
-       uint16_t        index;
-       uint16_t        length;
-} __xdata ao_usb_setup;
-
-__xdata uint8_t ao_usb_ep0_state;
-uint8_t * __xdata ao_usb_ep0_in_data;
-__xdata uint8_t ao_usb_ep0_in_len;
-__xdata uint8_t        ao_usb_ep0_in_buf[2];
-__xdata uint8_t ao_usb_ep0_out_len;
-__xdata uint8_t *__data ao_usb_ep0_out_data;
-__xdata uint8_t ao_usb_configuration;
-
-/* Send an IN data packet */
-static void
-ao_usb_ep0_flush(void)
-{
-       __xdata uint8_t this_len;
-       __xdata uint8_t cs0;
-
-       USBINDEX = 0;
-       cs0 = USBCS0;
-       if (cs0 & USBCS0_INPKT_RDY)
-               ao_panic(0);
-
-       this_len = ao_usb_ep0_in_len;
-       if (this_len > AO_USB_CONTROL_SIZE)
-               this_len = AO_USB_CONTROL_SIZE;
-       cs0 = USBCS0_INPKT_RDY;
-       if (this_len != AO_USB_CONTROL_SIZE) {
-               cs0 = USBCS0_INPKT_RDY | USBCS0_DATA_END;
-               ao_usb_ep0_state = AO_USB_EP0_IDLE;
-       }
-       ao_usb_ep0_in_len -= this_len;
-       while (this_len--)
-               USBFIFO[0] = *ao_usb_ep0_in_data++;
-       USBINDEX = 0;
-       USBCS0 = cs0;
-}
-
-__xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
-
-/* Walk through the list of descriptors and find a match
- */
-static void
-ao_usb_get_descriptor(uint16_t value)
-{
-       const uint8_t           *__xdata descriptor;
-       __xdata uint8_t         type = value >> 8;
-       __xdata uint8_t         index = value;
-
-       descriptor = ao_usb_descriptors;
-       while (descriptor[0] != 0) {
-               if (descriptor[1] == type && index-- == 0) {
-                       if (type == AO_USB_DESC_CONFIGURATION)
-                               ao_usb_ep0_in_len = descriptor[2];
-                       else
-                               ao_usb_ep0_in_len = descriptor[0];
-                       ao_usb_ep0_in_data = descriptor;
-                       break;
-               }
-               descriptor += descriptor[0];
-       }
-}
-
-/* Read data from the ep0 OUT fifo
- */
-static void
-ao_usb_ep0_fill(void)
-{
-       __xdata uint8_t len;
-
-       USBINDEX = 0;
-       len = USBCNT0;
-       if (len > ao_usb_ep0_out_len)
-               len = ao_usb_ep0_out_len;
-       ao_usb_ep0_out_len -= len;
-       while (len--)
-               *ao_usb_ep0_out_data++ = USBFIFO[0];
-}
-
-void
-ao_usb_ep0_queue_byte(uint8_t a)
-{
-       ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
-}
-
-void
-ao_usb_set_address(uint8_t address)
-{
-       ao_usb_running = 1;
-       USBADDR = address | 0x80;
-       while (USBADDR & 0x80)
-               ;
-}
-
-static void
-ao_usb_set_configuration(void)
-{
-       /* Set the IN max packet size, double buffered */
-       USBINDEX = AO_USB_IN_EP;
-       USBMAXI = AO_USB_IN_SIZE >> 3;
-       USBCSIH |= USBCSIH_IN_DBL_BUF;
-
-       /* Set the OUT max packet size, double buffered */
-       USBINDEX = AO_USB_OUT_EP;
-       USBMAXO = AO_USB_OUT_SIZE >> 3;
-       USBCSOH = USBCSOH_OUT_DBL_BUF;
-}
-
-static void
-ao_usb_ep0_setup(void)
-{
-       /* Pull the setup packet out of the fifo */
-       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
-       ao_usb_ep0_out_len = 8;
-       ao_usb_ep0_fill();
-       if (ao_usb_ep0_out_len != 0)
-               return;
-
-       /* Figure out how to ACK the setup packet */
-       if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
-               if (ao_usb_setup.length)
-                       ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
-               else
-                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-       } else {
-               if (ao_usb_setup.length)
-                       ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
-               else
-                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-       }
-       USBINDEX = 0;
-       if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
-               USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
-       else
-               USBCS0 = USBCS0_CLR_OUTPKT_RDY;
-
-       ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
-       ao_usb_ep0_in_len = 0;
-       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
-       case AO_USB_TYPE_STANDARD:
-               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
-               case AO_USB_RECIP_DEVICE:
-                       switch(ao_usb_setup.request) {
-                       case AO_USB_REQ_GET_STATUS:
-                               ao_usb_ep0_queue_byte(0);
-                               ao_usb_ep0_queue_byte(0);
-                               break;
-                       case AO_USB_REQ_SET_ADDRESS:
-                               ao_usb_set_address(ao_usb_setup.value);
-                               break;
-                       case AO_USB_REQ_GET_DESCRIPTOR:
-                               ao_usb_get_descriptor(ao_usb_setup.value);
-                               break;
-                       case AO_USB_REQ_GET_CONFIGURATION:
-                               ao_usb_ep0_queue_byte(ao_usb_configuration);
-                               break;
-                       case AO_USB_REQ_SET_CONFIGURATION:
-                               ao_usb_configuration = ao_usb_setup.value;
-                               ao_usb_set_configuration();
-                               break;
-                       }
-                       break;
-               case AO_USB_RECIP_INTERFACE:
-                       #pragma disable_warning 110
-                       switch(ao_usb_setup.request) {
-                       case AO_USB_REQ_GET_STATUS:
-                               ao_usb_ep0_queue_byte(0);
-                               ao_usb_ep0_queue_byte(0);
-                               break;
-                       case AO_USB_REQ_GET_INTERFACE:
-                               ao_usb_ep0_queue_byte(0);
-                               break;
-                       case AO_USB_REQ_SET_INTERFACE:
-                               break;
-                       }
-                       break;
-               case AO_USB_RECIP_ENDPOINT:
-                       switch(ao_usb_setup.request) {
-                       case AO_USB_REQ_GET_STATUS:
-                               ao_usb_ep0_queue_byte(0);
-                               ao_usb_ep0_queue_byte(0);
-                               break;
-                       }
-                       break;
-               }
-               break;
-       case AO_USB_TYPE_CLASS:
-               switch (ao_usb_setup.request) {
-               case SET_LINE_CODING:
-                       ao_usb_ep0_out_len = 7;
-                       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
-                       break;
-               case GET_LINE_CODING:
-                       ao_usb_ep0_in_len = 7;
-                       ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
-                       break;
-               case SET_CONTROL_LINE_STATE:
-                       break;
-               }
-               break;
-       }
-       if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
-               if (ao_usb_setup.length < ao_usb_ep0_in_len)
-                       ao_usb_ep0_in_len = ao_usb_setup.length;
-               ao_usb_ep0_flush();
-       }
-}
-
-/* End point 0 receives all of the control messages. */
-static void
-ao_usb_ep0(void)
-{
-       __xdata uint8_t cs0;
-
-       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-       for (;;) {
-               __critical for (;;) {
-                       if (ao_usb_iif & 1) {
-                               ao_usb_iif &= ~1;
-                               break;
-                       }
-                       ao_sleep(&ao_usb_task);
-               }
-               USBINDEX = 0;
-               cs0 = USBCS0;
-               if (cs0 & USBCS0_SETUP_END) {
-                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-                       USBCS0 = USBCS0_CLR_SETUP_END;
-               }
-               if (cs0 & USBCS0_SENT_STALL) {
-                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-                       USBCS0 &= ~USBCS0_SENT_STALL;
-               }
-               if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
-                   (cs0 & USBCS0_INPKT_RDY) == 0)
-               {
-                       ao_usb_ep0_flush();
-               }
-               if (cs0 & USBCS0_OUTPKT_RDY) {
-                       switch (ao_usb_ep0_state) {
-                       case AO_USB_EP0_IDLE:
-                               ao_usb_ep0_setup();
-                               break;
-                       case AO_USB_EP0_DATA_OUT:
-                               ao_usb_ep0_fill();
-                               if (ao_usb_ep0_out_len == 0)
-                                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
-                               USBINDEX = 0;
-                               if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
-                                       USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
-                               else
-                                       USBCS0 = USBCS0_CLR_OUTPKT_RDY;
-                               break;
-                       }
-               }
-       }
-}
-
-void
-ao_usb_flush(void) __critical
-{
-       if (ao_usb_in_bytes) {
-               USBINDEX = AO_USB_IN_EP;
-               USBCSIL |= USBCSIL_INPKT_RDY;
-               ao_usb_in_bytes = 0;
-       }
-}
-
-void
-ao_usb_putchar(char c) __critical
-{
-       if (!ao_usb_running)
-               return;
-       for (;;) {
-               USBINDEX = AO_USB_IN_EP;
-               if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
-                       break;
-               ao_sleep(&ao_usb_in_bytes);
-       }
-       USBFIFO[AO_USB_IN_EP << 1] = c;
-       if (++ao_usb_in_bytes == AO_USB_IN_SIZE) {
-               USBINDEX = AO_USB_IN_EP;
-               USBCSIL |= USBCSIL_INPKT_RDY;
-               ao_usb_in_bytes = 0;
-       }
-}
-
-char
-ao_usb_getchar(void) __critical
-{
-       __xdata char    c;
-       while (ao_usb_out_bytes == 0) {
-               for (;;) {
-                       USBINDEX = AO_USB_OUT_EP;
-                       if ((USBCSOL & USBCSOL_OUTPKT_RDY) != 0)
-                               break;
-                       ao_sleep(&ao_usb_out_bytes);
-               }
-               ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
-       }
-       --ao_usb_out_bytes;
-       c = USBFIFO[AO_USB_OUT_EP << 1];
-       if (ao_usb_out_bytes == 0) {
-               USBINDEX = AO_USB_OUT_EP;
-               USBCSOL &= ~USBCSOL_OUTPKT_RDY;
-       }
-       return c;
-}
-
-void
-ao_usb_enable(void)
-{
-       /* Turn on the USB controller */
-       SLEEP |= SLEEP_USB_EN;
-
-       ao_usb_set_configuration();
-
-       ao_usb_set_interrupts();
-
-       /* enable USB interrupts */
-       IEN2 |= IEN2_USBIE;
-
-       /* Clear any pending interrupts */
-       USBCIF = 0;
-       USBOIF = 0;
-       USBIIF = 0;
-}
-
-void
-ao_usb_disable(void)
-{
-       /* Disable USB interrupts */
-       USBIIE = 0;
-       USBOIE = 0;
-       USBCIE = 0;
-       IEN2 &= ~IEN2_USBIE;
-
-       /* Clear any pending interrupts */
-       USBCIF = 0;
-       USBOIF = 0;
-       USBIIF = 0;
-
-       /* Turn off the USB controller */
-       SLEEP &= ~SLEEP_USB_EN;
-}
-
-void
-ao_usb_init(void)
-{
-       ao_usb_enable();
-
-       ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");
-}
diff --git a/src/ao_usb.h b/src/ao_usb.h
deleted file mode 100644 (file)
index 6633daf..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright © 2009 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-#ifndef _AO_USB_H_
-#define _AO_USB_H_
-
-#define AO_USB_SETUP_DIR_MASK  (0x01 << 7)
-#define AO_USB_SETUP_TYPE_MASK (0x03 << 5)
-#define AO_USB_SETUP_RECIP_MASK        (0x1f)
-
-#define AO_USB_DIR_OUT                 0
-#define AO_USB_DIR_IN                  (1 << 7)
-
-#define AO_USB_TYPE_STANDARD           0
-#define AO_USB_TYPE_CLASS              (1 << 5)
-#define AO_USB_TYPE_VENDOR             (2 << 5)
-#define AO_USB_TYPE_RESERVED           (3 << 5)
-
-#define AO_USB_RECIP_DEVICE            0
-#define AO_USB_RECIP_INTERFACE         1
-#define AO_USB_RECIP_ENDPOINT          2
-#define AO_USB_RECIP_OTHER             3
-
-/* standard requests */
-#define        AO_USB_REQ_GET_STATUS           0x00
-#define AO_USB_REQ_CLEAR_FEATURE       0x01
-#define AO_USB_REQ_SET_FEATURE         0x03
-#define AO_USB_REQ_SET_ADDRESS         0x05
-#define AO_USB_REQ_GET_DESCRIPTOR      0x06
-#define AO_USB_REQ_SET_DESCRIPTOR      0x07
-#define AO_USB_REQ_GET_CONFIGURATION   0x08
-#define AO_USB_REQ_SET_CONFIGURATION   0x09
-#define AO_USB_REQ_GET_INTERFACE       0x0A
-#define AO_USB_REQ_SET_INTERFACE       0x0B
-#define AO_USB_REQ_SYNCH_FRAME         0x0C
-
-#define AO_USB_DESC_DEVICE             1
-#define AO_USB_DESC_CONFIGURATION      2
-#define AO_USB_DESC_STRING             3
-#define AO_USB_DESC_INTERFACE          4
-#define AO_USB_DESC_ENDPOINT           5
-#define AO_USB_DESC_DEVICE_QUALIFIER   6
-#define AO_USB_DESC_OTHER_SPEED                7
-#define AO_USB_DESC_INTERFACE_POWER    8
-
-#define AO_USB_GET_DESC_TYPE(x)                (((x)>>8)&0xFF)
-#define AO_USB_GET_DESC_INDEX(x)       ((x)&0xFF)
-
-#define AO_USB_CONTROL_EP      0
-#define AO_USB_INT_EP          1
-#define AO_USB_OUT_EP          4
-#define AO_USB_IN_EP           5
-#define AO_USB_CONTROL_SIZE    32
-/*
- * Double buffer IN and OUT EPs, so each
- * gets half of the available space
- *
- * Ah, but USB bulk packets can only come in 8, 16, 32 and 64
- * byte sizes, so we'll use 64 for everything
- */
-#define AO_USB_IN_SIZE         64
-#define AO_USB_OUT_SIZE                64
-
-#define AO_USB_EP0_IDLE                0
-#define AO_USB_EP0_DATA_IN     1
-#define AO_USB_EP0_DATA_OUT    2
-
-#define LE_WORD(x)    ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8))
-
-/* CDC definitions */
-#define CS_INTERFACE      0x24
-#define CS_ENDPOINT       0x25
-
-#define SET_LINE_CODING         0x20
-#define GET_LINE_CODING         0x21
-#define SET_CONTROL_LINE_STATE  0x22
-
-/* Data structure for GET_LINE_CODING / SET_LINE_CODING class requests */
-struct ao_usb_line_coding {
-       uint32_t        rate;
-       uint8_t         char_format;
-       uint8_t         parity;
-       uint8_t         data_bits;
-} ;
-
-#endif /* _AO_USB_H_ */
diff --git a/src/avr-demo/.gitignore b/src/avr-demo/.gitignore
new file mode 100644 (file)
index 0000000..e7d1634
--- /dev/null
@@ -0,0 +1,3 @@
+avr-demo
+avr-demo.hex
+ao_product.h
diff --git a/src/avr-demo/Makefile b/src/avr-demo/Makefile
new file mode 100644 (file)
index 0000000..9329516
--- /dev/null
@@ -0,0 +1,105 @@
+#
+# AltOS build
+#
+#
+vpath % ..:../core:../product:../drivers:../avr
+vpath make-altitude ..
+vpath make-kalman ..
+vpath kalman.5c ../kalman
+vpath kalman_filter.5c ../kalman
+vpath load_csv.5c ../kalman
+vpath matrix.5c ../kalman
+vpath ao-make-product.5c ../util
+
+MCU=atmega32u4
+DUDECPUTYPE=m32u4
+#PROGRAMMER=stk500v2 -P usb
+PROGRAMMER=usbtiny
+LOADCMD=avrdude
+LOADARG=-p $(DUDECPUTYPE) -c $(PROGRAMMER) -e -U flash:w:
+CC=avr-gcc
+OBJCOPY=avr-objcopy
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       altitude.h \
+       ao_kalman.h
+
+#
+# Common AltOS sources
+#
+ALTOS_SRC = \
+       ao_cmd.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_serial_avr.c \
+       ao_avr_stdio.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_timer.c \
+       ao_led.c \
+       ao_usb_avr.c \
+       ao_lcd.c
+
+PRODUCT=AvrDemo-v0.0
+MCU=atmega32u4
+PRODUCT_DEF=-DAVR_DEMO
+IDPRODUCT=0x000a
+CFLAGS = $(PRODUCT_DEF) -I. -I../avr -I../core -I..
+CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -Os -mcall-prologues
+
+NICKLE=nickle
+
+PROG=avr-demo
+
+SRC=$(ALTOS_SRC) ao_demo.c ao_debug_avr.c
+OBJ=$(SRC:.c=.o)
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ)
+
+$(PROG).hex: $(PROG)
+       avr-size $(PROG)
+       $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@
+
+
+load: $(PROG).hex
+       $(LOADCMD) $(LOADARG)$(PROG).hex
+
+../altitude.h: make-altitude
+       nickle $< > $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+ao_product.rel: ao_product.c ao_product.h
+       $(call quiet,CC) -c $(CFLAGS) -D PRODUCT_DEFS='\"ao_product.h\"' -o$@ $<
+
+distclean:     clean
+
+clean:
+       rm -f $(OBJ)
+       rm -f ao_product.h
+
+install:
+
+uninstall:
+
+$(OBJ): ao.h ao_product.h
\ No newline at end of file
diff --git a/src/avr-demo/ao_demo.c b/src/avr-demo/ao_demo.c
new file mode 100644 (file)
index 0000000..756dd0d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+struct ao_task demo_task;
+
+void
+ao_demo(void)
+{
+       for (;;) {
+               ao_led_toggle(AO_LED_RED);
+               printf ("hello %d\n", ao_time());
+               ao_delay(AO_MS_TO_TICKS(200));
+       }
+}
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       ao_serial_init();
+
+       ao_led_init(LEDS_AVAILABLE);
+       ao_avr_stdio_init();
+       printf ("stdio initialized\n");
+//     ao_debug_init();
+       ao_timer_init();
+       ao_cmd_init();
+       ao_usb_init();
+       ao_lcd_init();
+
+//     ao_add_task(&demo_task, ao_demo, "demo");
+       /* Turn on the LED until the system is stable */
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/avr/ao_adc_avr.c b/src/avr/ao_adc_avr.c
new file mode 100644 (file)
index 0000000..3a26297
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+volatile __xdata struct ao_data        ao_data_ring[AO_DATA_RING];
+volatile __data uint8_t                ao_data_head;
+
+#ifdef TELESCIENCE
+const uint8_t  adc_channels[AO_LOG_TELESCIENCE_NUM_ADC] = {
+       0x00,
+       0x01,
+       0x04,
+       0x05,
+       0x06,
+       0x07,
+       0x20,
+       0x21,
+       0x22,
+       0x23,
+       0x24,
+       0x25,
+};
+#endif
+
+#ifdef TELEPYRO
+const uint8_t  adc_channels[AO_TELEPYRO_NUM_ADC] = {
+       0x00,   /* ADC0  v_batt */
+       0x04,   /* ADC4  sense_a */
+       0x05,   /* ADC5  sense_b */
+       0x06,   /* ADC6  sense_c */
+       0x07,   /* ADC7  sense_d */
+       0x23,   /* ADC11 sense_e */
+       0x22,   /* ADC10 sense_f */
+       0x21,   /* ADC9 sense_g */
+       0x20,   /* ADC8 sense_h */
+};
+#endif
+
+#define NUM_ADC        (sizeof (adc_channels) / sizeof (adc_channels[0]))
+
+static uint8_t ao_adc_channel;
+
+#define ADC_CHANNEL_LOW(c)     (((c) & 0x1f) << MUX0)
+#define ADC_CHANNEL_HIGH(c)    ((((c) & 0x20) >> 5) << MUX5)
+
+#define ADCSRA_INIT    ((1 << ADEN) |          /* Enable ADC */                \
+                        (0 << ADATE) |         /* No auto ADC trigger */       \
+                        (1 << ADIF) |          /* Clear interrupt */           \
+                        (0 << ADIE) |          /* Enable interrupt */          \
+                        (6 << ADPS0))          /* Prescale clock by 64 */
+
+#define ADCSRB_INIT    ((0 << ADHSM) |         /* No high-speed mode */ \
+                        (0 << ACME) |          /* Some comparitor thing */ \
+                        (0 << ADTS0))          /* Free running mode (don't care) */
+
+static void
+ao_adc_start(void)
+{
+       uint8_t channel = adc_channels[ao_adc_channel];
+       ADMUX = ((0 << REFS1) |                         /* AVcc reference */
+                (1 << REFS0) |                         /* AVcc reference */
+                (1 << ADLAR) |                         /* Left-shift results */
+                (ADC_CHANNEL_LOW(channel)));           /* Select channel */
+
+       ADCSRB = (ADCSRB_INIT |
+                 ADC_CHANNEL_HIGH(channel));           /* High channel bit */
+
+       ADCSRA = (ADCSRA_INIT |
+                 (1 << ADSC) |
+                 (1 << ADIE));                         /* Start conversion */
+}
+
+ISR(ADC_vect)
+{
+       uint16_t        value;
+
+       /* Must read ADCL first or the value there will be lost */
+       value = ADCL;
+       value |= (ADCH << 8);
+       ao_data_ring[ao_data_head].adc.adc[ao_adc_channel] = value;
+       if (++ao_adc_channel < NUM_ADC)
+               ao_adc_start();
+       else {
+               ADCSRA = ADCSRA_INIT;
+               ao_data_ring[ao_data_head].tick = ao_time();
+               ao_data_head = ao_data_ring_next(ao_data_head);
+               ao_wakeup((void *) &ao_data_head);
+               ao_cpu_sleep_disable = 0;
+       }
+}
+
+void
+ao_adc_poll(void)
+{
+       ao_cpu_sleep_disable = 1;
+       ao_adc_channel = 0;
+       ao_adc_start();
+}
+
+void
+ao_data_get(__xdata struct ao_data *packet)
+{
+       uint8_t i = ao_data_ring_prev(ao_data_head);
+       memcpy(packet, (void *) &ao_data_ring[i], sizeof (struct ao_data));
+}
+
+static void
+ao_adc_dump(void) __reentrant
+{
+       static __xdata struct ao_data   packet;
+       uint8_t i;
+       ao_data_get(&packet);
+       printf("tick: %5u",  packet.tick);
+       for (i = 0; i < NUM_ADC; i++)
+               printf (" %2d: %5u", i, packet.adc.adc[i]);
+       printf("\n");
+}
+
+__code struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0ADC" },
+       { 0, NULL },
+};
+
+void
+ao_adc_init(void)
+{
+       PRR0 &= ~(1 << PRADC);
+       DIDR0 = 0xf3;
+       DIDR2 = 0x3f;
+       ADCSRB = ADCSRB_INIT;
+       ADCSRA = ADCSRA_INIT;
+       ao_cmd_register(&ao_adc_cmds[0]);
+}
diff --git a/src/avr/ao_arch.h b/src/avr/ao_arch.h
new file mode 100644 (file)
index 0000000..a14d0ad
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stdio.h>
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <avr/sleep.h>
+
+#ifdef AVR_DEMO
+#define TEENSY 1
+#endif
+
+#if TEENSY
+#define F_CPU 16000000UL       // 16 MHz
+#else
+#define F_CPU  8000000UL       // 8 MHz
+#endif
+
+/*
+ * AVR definitions and code fragments for AltOS
+ */
+
+#define AO_STACK_SIZE  116
+
+/* Various definitions to make GCC look more like SDCC */
+
+#define ao_arch_naked_declare  __attribute__((naked))
+#define ao_arch_naked_define
+#define __pdata
+#define __data
+#define __xdata
+#define __code const
+#define __reentrant
+#define __critical
+#define __interrupt(n)
+#define __at(n)
+
+#define ao_arch_reboot()       /* XXX */
+
+#define ao_arch_nop()          asm("nop")
+
+#define ao_arch_interrupt(n)   /* nothing */
+
+#undef putchar
+#undef getchar
+#define putchar(c)     ao_putchar(c)
+#define getchar                ao_getchar
+
+extern void putchar(char c);
+extern char getchar(void);
+extern void ao_avr_stdio_init(void);
+
+#define AO_ROMCONFIG_VERSION   2
+
+#define AO_ROMCONFIG_SYMBOL(a) const
+
+extern AO_ROMCONFIG_SYMBOL(0) uint16_t ao_serial_number;
+
+#define AVR_PUSH8(stack, val)  (*((stack)--) = (val))
+
+extern uint8_t ao_cpu_sleep_disable;
+
+#define ao_arch_task_globals   uint8_t ao_cpu_sleep_disable;
+
+#define ao_arch_task_members\
+       uint8_t *sp;                    /* saved stack pointer */
+
+#define ao_arch_init_stack(task, start) do {                   \
+       uint8_t         *sp = task->stack + AO_STACK_SIZE - 1;  \
+       uint16_t        a = (uint16_t) start;                   \
+       int             i;                                      \
+                                                               \
+       /* Return address */                                    \
+       AVR_PUSH8(sp, a);                                       \
+       AVR_PUSH8(sp, (a >> 8));                                \
+                                                               \
+       /* Clear register values */                             \
+       i = 32;                                                 \
+       while (i--)                                             \
+               AVR_PUSH8(sp, 0);                               \
+                                                               \
+       /* SREG with interrupts enabled */                      \
+       AVR_PUSH8(sp, 0x80);                                    \
+       task->sp = sp;                                          \
+} while (0);
+       
+#define ao_arch_save_regs() do {                                       \
+               asm("push r31" "\n\t" "push r30");                      \
+               asm("push r29" "\n\t" "push r28" "\n\t" "push r27" "\n\t" "push r26" "\n\t" "push r25"); \
+               asm("push r24" "\n\t" "push r23" "\n\t" "push r22" "\n\t" "push r21" "\n\t" "push r20"); \
+               asm("push r19" "\n\t" "push r18" "\n\t" "push r17" "\n\t" "push r16" "\n\t" "push r15"); \
+               asm("push r14" "\n\t" "push r13" "\n\t" "push r12" "\n\t" "push r11" "\n\t" "push r10"); \
+               asm("push r9" "\n\t" "push r8" "\n\t" "push r7" "\n\t" "push r6" "\n\t" "push r5"); \
+               asm("push r4" "\n\t" "push r3" "\n\t" "push r2" "\n\t" "push r1" "\n\t" "push r0"); \
+               asm("in r0, __SREG__" "\n\t" "push r0");                \
+               sei();                                                  \
+       } while (0)
+
+#define ao_arch_save_stack() do {                                      \
+               uint8_t sp_l, sp_h;                                     \
+               asm("in %0,__SP_L__" : "=&r" (sp_l) );                  \
+               asm("in %0,__SP_H__" : "=&r" (sp_h) );                  \
+               ao_cur_task->sp = (uint8_t *) ((uint16_t) sp_l | ((uint16_t) sp_h << 8)); \
+       } while (0)
+
+#define ao_arch_isr_stack()    /* nothing */
+
+#define ao_arch_cpu_idle() do {                        \
+               if (!ao_cpu_sleep_disable)      \
+                       sleep_cpu();            \
+       } while (0)
+
+#define ao_arch_restore_stack() do { \
+               uint8_t sp_l, sp_h;                                     \
+               sp_l = (uint16_t) ao_cur_task->sp;                      \
+               sp_h = ((uint16_t) ao_cur_task->sp) >> 8;               \
+               cli();                                                  \
+               asm("out __SP_H__,%0" : : "r" (sp_h) );                 \
+               asm("out __SP_L__,%0" : : "r" (sp_l) );                 \
+               asm("pop r0"    "\n\t"                                  \
+                   "out __SREG__, r0");                                \
+               asm("pop r0" "\n\t" "pop r1" "\n\t" "pop r2" "\n\t" "pop r3" "\n\t" "pop r4"); \
+               asm("pop r5" "\n\t" "pop r6" "\n\t" "pop r7" "\n\t" "pop r8" "\n\t" "pop r9"); \
+               asm("pop r10" "\n\t" "pop r11" "\n\t" "pop r12" "\n\t" "pop r13" "\n\t" "pop r14"); \
+               asm("pop r15" "\n\t" "pop r16" "\n\t" "pop r17" "\n\t" "pop r18" "\n\t" "pop r19"); \
+               asm("pop r20" "\n\t" "pop r21" "\n\t" "pop r22" "\n\t" "pop r23" "\n\t" "pop r24"); \
+               asm("pop r25" "\n\t" "pop r26" "\n\t" "pop r27" "\n\t" "pop r28" "\n\t" "pop r29"); \
+               asm("pop r30" "\n\t" "pop r31");                        \
+               asm("ret");                                             \
+       } while(0)
+
+#define ao_arch_critical(b) do { cli(); do { b } while (0); sei(); } while (0)
+
+#define AO_TELESCIENCE_NUM_ADC 12
+
+#endif /* _AO_ARCH_H_ */
+
diff --git a/src/avr/ao_arch_funcs.h b/src/avr/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..792ff74
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+/*
+ * ao_spi.c
+ */
+
+extern __xdata uint8_t ao_spi_mutex;
+
+#define ao_spi_get_mask(reg,mask,bus,speed) do {       \
+               ao_mutex_get(&ao_spi_mutex);    \
+               (reg) &= ~(mask);               \
+       } while (0)
+
+#define ao_spi_put_mask(reg,mask,bus) do {     \
+               (reg) |= (mask);                \
+               ao_mutex_put(&ao_spi_mutex);    \
+       } while (0)
+
+#define ao_spi_get_bit(reg,bit,pin,bus,speed) do {     \
+               ao_mutex_get(&ao_spi_mutex);    \
+               (pin) = 0;                      \
+       } while (0)
+
+#define ao_spi_put_bit(reg,bit,pin,bus) do {   \
+               (pin) = 1;                      \
+               ao_mutex_put(&ao_spi_mutex);    \
+       } while (0)
+
+
+#define ao_gpio_token_paster(x,y)              x ## y
+#define ao_gpio_token_evaluator(x,y)   ao_gpio_token_paster(x,y)
+
+#define ao_gpio_set(port, bit, pin, v) do {                            \
+               if (v)                                                  \
+                       (ao_gpio_token_evaluator(PORT,port)) |= (1 << bit); \
+               else                                                    \
+                       (ao_gpio_token_evaluator(PORT,port)) &= ~(1 << bit); \
+       } while (0)
+
+/*
+ * The SPI mutex must be held to call either of these
+ * functions -- this mutex covers the entire SPI operation,
+ * from chip select low to chip select high
+ */
+
+#define ao_enable_output(port, bit, pin, v) do {                       \
+               ao_gpio_set(port, bit, pin, v);                         \
+               ao_gpio_token_evaluator(DDR,port) |= (1 << bit);        \
+       } while (0)
+
+
+void
+ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant;
+
+void
+ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant;
+
+#define ao_spi_send(block, len, bus) ao_spi_send_bus(block, len)
+#define ao_spi_recv(block, len, bus) ao_spi_recv_bus(block, len)
+
+void
+ao_spi_init(void);
+
+#define ao_spi_init_cs(port, mask) do {                \
+               SPI_CS_PORT |= (mask);          \
+               SPI_CS_DIR |= (mask);           \
+       } while (0)
diff --git a/src/avr/ao_avr_stdio.c b/src/avr/ao_avr_stdio.c
new file mode 100644 (file)
index 0000000..2765853
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+int
+stdio_put(char c, FILE *stream)
+{
+       if (ao_cur_task && ao_num_stdios)
+               putchar(c);
+       else
+       {
+               if (c == '\n')
+                       stdio_put('\r', stream);
+               loop_until_bit_is_set(UCSR1A, UDRE1);
+               UDR1 = c;
+       }
+
+       return 0;
+}
+
+int
+stdio_get(FILE *stream)
+{
+       return (int) getchar() & 0xff;
+}
+
+static FILE mystdout = FDEV_SETUP_STREAM(stdio_put, NULL, _FDEV_SETUP_WRITE);
+
+static FILE mystdin = FDEV_SETUP_STREAM(NULL, stdio_get, _FDEV_SETUP_READ);
+
+void
+ao_avr_stdio_init(void)
+{
+       stdout = &mystdout;
+       stdin = &mystdin;
+}
diff --git a/src/avr/ao_clock.c b/src/avr/ao_clock.c
new file mode 100644 (file)
index 0000000..0d42b6d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/*
+ * AltOS always cranks the clock to the max frequency
+ */
+
+void
+ao_clock_init(void)
+{
+       /* disable RC clock */
+       CLKSEL0 &= ~(1 << RCE);
+
+       /* Disable PLL */
+       PLLCSR &= ~(1 << PLLE);
+
+       /* Enable external clock */
+       CLKSEL0 |= (1 << EXTE);
+
+       /* wait for external clock to be ready */
+       while ((CLKSTA & (1 << EXTON)) == 0)
+               ;
+
+       /* select external clock */
+       CLKSEL0 |= (1 << CLKS);
+
+       /* Disable the clock prescaler */
+       cli();
+       CLKPR = (1 << CLKPCE);
+
+       /* Always run the system clock at 8MHz */
+#if AVR_CLOCK > 12000000UL
+       CLKPR = 1;
+#else
+       CLKPR = 0;
+#endif
+       sei();
+
+       /* Set up the PLL to use the crystal */
+
+       /* Use primary system clock as PLL source */
+       PLLFRQ = ((0 << PINMUX) |       /* Use primary clock */
+                 (0 << PLLUSB) |       /* No divide by 2 for USB */
+                 (0 << PLLTM0) |       /* Disable high speed timer */
+                 (0x4 << PDIV0));      /* 48MHz PLL clock */
+
+       /* Set the frequency of the crystal */
+#if AVR_CLOCK > 12000000UL
+       PLLCSR |= (1 << PINDIV);        /* For 16MHz crystal on Teensy board */
+#else
+       PLLCSR &= ~(1 << PINDIV);       /* For 8MHz crystal on TeleScience board */
+#endif
+
+       /* Enable the PLL */
+       PLLCSR |= (1 << PLLE);
+       while (!(PLLCSR & (1 << PLOCK)))
+               ;
+
+       set_sleep_mode(SLEEP_MODE_IDLE);
+       sleep_enable();
+}
diff --git a/src/avr/ao_debug_avr.c b/src/avr/ao_debug_avr.c
new file mode 100644 (file)
index 0000000..2e41e15
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+uart_send(char c)
+{
+       loop_until_bit_is_set(UCSR1A, UDRE1);
+       UDR1 = c;
+}
+
+int
+uart_put(char c, FILE *stream)
+{
+       if (c == '\n')
+               uart_send('\r');
+       uart_send(c);
+       return 0;
+}
+
+int
+uart_get(FILE *stream)
+{
+       loop_until_bit_is_set(UCSR1A, RXC1);
+       return (int) UDR1 & 0xff;
+}
+
+void
+uart_init(uint16_t baud)
+{
+       PRR1 &= ~(1 << PRUSART1);
+       UBRR1L = baud;
+       UBRR1H = baud >> 8;
+       UCSR1A = 0;
+       UCSR1B = ((1 << RXEN1) |        /* Enable receiver */
+                 (1 << TXEN1));        /* Enable transmitter */
+       UCSR1C = ((0 << UMSEL10) |      /* Asynchronous mode */
+                 (0 << UPM10) |        /* No parity */
+                 (0 << USBS1) |        /* 1 stop bit */
+                 (3 << UCSZ10) |       /* 8 bit characters */
+                 (0 << UCPOL1));       /* MBZ for async mode */
+}
+
+static FILE mystdout = FDEV_SETUP_STREAM(uart_put, NULL, _FDEV_SETUP_WRITE);
+
+static FILE mystdin = FDEV_SETUP_STREAM(NULL, uart_get, _FDEV_SETUP_READ);
+
+void   ao_debug_init(void)
+{
+       uart_init(F_CPU / (16UL * 9600UL) - 1);
+
+       stdout = &mystdout;
+       stdin = &mystdin;
+
+       if (DDRB & AO_LED_RED) {
+               printf ("oops, starting all over\n");
+               for (;;)
+                       ;
+       }
+       DDRB |= (1 << 7);
+       PORTB |= (1 << 7);
+       printf ("debug initialized\n");
+}
diff --git a/src/avr/ao_eeprom_avr.c b/src/avr/ao_eeprom_avr.c
new file mode 100644 (file)
index 0000000..2451fa8
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ * Copyright © 2011  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.
+ */
+
+#include <ao.h>
+#include <ao_storage.h>
+
+/* Total bytes of available storage */
+__pdata ao_pos_t       ao_storage_total = 1024;
+
+/* Block size - device is erased in these units. */
+__pdata ao_pos_t       ao_storage_block = 1024;
+
+/* Byte offset of config block. Will be ao_storage_block bytes long */
+__pdata ao_pos_t       ao_storage_config = 0;
+
+/* Storage unit size - device reads and writes must be within blocks of this size. */
+__pdata uint16_t       ao_storage_unit = 1024;
+
+/*
+ * The internal flash chip is arranged in 8 byte sectors; the
+ * chip cannot erase in units smaller than that.
+ *
+ * Writing happens in units of 2 bytes and
+ * can only change bits from 1 to 0. So, you can rewrite
+ * the same contents, or append to an existing page easily enough
+ */
+
+/*
+ * Erase the specified sector
+ */
+uint8_t
+ao_storage_erase(ao_pos_t pos) __reentrant
+{
+       /* Not necessary */
+       return 1;
+}
+
+#define ao_intflash_wait_idle() do {                                   \
+               /* Wait for any outstanding writes to complete */       \
+               while (EECR & (1 << EEPE))                              \
+                       ;                                               \
+       } while (0)                                                     \
+
+static void
+ao_intflash_write(uint16_t pos, uint8_t d)
+{
+       ao_intflash_wait_idle();
+       EEAR = pos;
+       EEDR = d;
+       ao_arch_critical(
+               EECR |= (1 << EEMPE);
+               EECR |= (1 << EEPE);
+               );
+}
+
+static uint8_t
+ao_intflash_read(uint16_t pos)
+{
+       ao_intflash_wait_idle();
+       EEAR = pos;
+
+       EECR |= (1 << EERE);
+       return EEDR;
+}
+/*
+ * Write to flash
+ */
+
+uint8_t
+ao_storage_device_write(ao_pos_t pos32, __xdata void *v, uint16_t len) __reentrant
+{
+       uint16_t pos = pos32;
+       __xdata uint8_t *d = v;
+
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+
+       while (len--)
+               ao_intflash_write(pos++, *d++);
+
+       return 1;
+}
+
+/*
+ * Read from flash
+ */
+uint8_t
+ao_storage_device_read(ao_pos_t pos, __xdata void *v, uint16_t len) __reentrant
+{
+       uint8_t *d = v;
+       
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+       while (len--)
+               *d++ = ao_intflash_read(pos++);
+       return 1;
+}
+
+void
+ao_storage_flush(void) __reentrant
+{
+}
+
+void
+ao_storage_setup(void)
+{
+}
+
+void
+ao_storage_device_info(void) __reentrant
+{
+       printf ("Using internal flash\n");
+}
+
+void
+ao_storage_device_init(void)
+{
+}
diff --git a/src/avr/ao_i2c_usart.c b/src/avr/ao_i2c_usart.c
new file mode 100644 (file)
index 0000000..60e35f8
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/*
+ * Atmega32u4 TWI master mode (I2C)
+ */
+
+__xdata uint8_t        ao_i2c_mutex;
+
+/* Send bytes over I2C.
+ *
+ * This just polls; the I2C is set to go as fast as possible,
+ * so using interrupts would take way too long
+ */
+void
+ao_i2c_send(void __xdata *block, uint16_t len) __reentrant
+{
+       uint8_t *d = block;
+
+       ao_mutex_get(&ao_i2c_mutex);
+       while (len--) {
+               while (!(UCSR1A & (1 << UDRE1)));
+               UDR1 = *d++;
+               while (!(UCSR1A & (1 << RXC1)));
+               (void) UDR1;
+       }
+       ao_mutex_put(&ao_i2c_mutex);
+}
+
+/* Receive bytes over I2C.
+ *
+ * This sets up tow DMA engines, one reading the data and another
+ * writing constant values to the I2C transmitter as that is what
+ * clocks the data coming in.
+ */
+void
+ao_i2c_recv(void __xdata *block, uint16_t len) __reentrant
+{
+       uint8_t *d = block;
+
+       ao_mutex_get(&ao_i2c_mutex);
+       while (len--) {
+               while (!(UCSR1A & (1 << UDRE1)));
+               UDR1 = 0;
+               while (!(UCSR1A & (1 << RXC1)));
+               *d++ = UDR1;
+       }
+       ao_mutex_put(&ao_i2c_mutex);
+}
+
+#define XCK1_DDR       DDRD
+#define XCK1_PORT      PORTD
+#define XCK1           PORTD5
+#define XMS1_DDR       DDRE
+#define XMS1_PORT      PORTE
+#define XMS1           PORTE6
+
+void
+ao_i2c_init(void)
+{
+       /* Ensure the TWI is powered */
+
+       /*
+        * Set pin directions
+        */
+       XCK1_DDR |= (1 << XCK1);
+
+       /* Clear chip select (which is negated) */
+       XMS1_PORT |= (1 < XMS1);
+       XMS1_DDR |= (1 << XMS1);
+
+       /* Set baud register to zero (required before turning transmitter on) */
+       UBRR1 = 0;
+
+       UCSR1C = ((0x3 << UMSEL10) |    /* Master I2C mode */
+                 (0 << UCSZ10) |       /* I2C mode 0 */
+                 (0 << UCPOL1));       /* I2C mode 0 */
+
+       /* Enable transmitter and receiver */
+       UCSR1B = ((1 << RXEN1) |
+                 (1 << TXEN1));
+
+       /* It says that 0 is a legal value; we'll see... */
+       UBRR1 = 0;
+}
diff --git a/src/avr/ao_lcd_port.c b/src/avr/ao_lcd_port.c
new file mode 100644 (file)
index 0000000..b1e8aa1
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#define LCD_PORT       PORTB
+#define LCD_DDR                DDRB
+
+#define PIN_RS         4
+#define PIN_E          5
+#define PIN_RW         6
+
+static void
+ao_lcd_port_set_bits(uint8_t bits)
+{
+#if 0
+       printf("\tLCD data %x RS %d R/W %d E %d\n",
+              bits & 0xf,
+              (bits & (1 << PIN_RS)) ? 1 : 0,
+              (bits & (1 << PIN_RW)) ? 1 : 0,
+              (bits & (1 << PIN_E)) ? 1 : 0);
+#endif
+       LCD_PORT = bits;
+#if 0
+       ao_delay(1);
+       if (bits & (1 << PIN_RW))
+               printf("\tLCD input %x\n", PINB);
+#endif
+}
+
+uint8_t
+ao_lcd_port_get_nibble(uint8_t rs)
+{
+       uint8_t data = (rs ? (1 << PIN_RS) : 0) | (1 << PIN_RW);
+       uint8_t n;
+
+       DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW);
+       ao_lcd_port_set_bits(data);
+       ao_lcd_port_set_bits(data | (1 << PIN_E));
+       n = PINB & 0xf;
+       ao_lcd_port_set_bits(data);
+       return n;
+}
+
+void
+ao_lcd_port_put_nibble(uint8_t rs, uint8_t data)
+{
+       data = (data & 0xf) | (rs ? (1 << PIN_RS) : 0);
+       DDRB = (0xf) | (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW);
+       ao_lcd_port_set_bits(data);
+       ao_lcd_port_set_bits(data | (1 << PIN_E));
+       ao_lcd_port_set_bits(data);
+}
+
+void
+ao_lcd_port_init(void)
+{
+       DDRB = (1 << PIN_RS) | (1 << PIN_E) | (1 << PIN_RW);
+       PORTB = 0;
+}
diff --git a/src/avr/ao_led.c b/src/avr/ao_led.c
new file mode 100644 (file)
index 0000000..91dfb85
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+__pdata uint8_t ao_led_enable;
+
+#define LED_PORT       PORTB
+#define LED_DDR                DDRB
+
+void
+ao_led_on(uint8_t colors)
+{
+       LED_PORT |= (colors & ao_led_enable);
+}
+
+void
+ao_led_off(uint8_t colors)
+{
+       LED_PORT &= ~(colors & ao_led_enable);
+}
+
+void
+ao_led_set(uint8_t colors)
+{
+       LED_PORT = (LED_PORT & ~(ao_led_enable)) | (colors & ao_led_enable);
+}
+
+void
+ao_led_toggle(uint8_t colors)
+{
+       LED_PORT ^= (colors & ao_led_enable);
+}
+
+void
+ao_led_for(uint8_t colors, uint16_t ticks) __reentrant
+{
+       ao_led_on(colors);
+       ao_delay(ticks);
+       ao_led_off(colors);
+}
+
+void
+ao_led_init(uint8_t enable)
+{
+       ao_led_enable = enable;
+       if ((LED_DDR & enable)) {
+               printf ("oops! restarted\n");
+               ao_panic(AO_PANIC_REBOOT);
+       }
+       LED_PORT &= ~enable;
+       LED_DDR |= enable;
+}
diff --git a/src/avr/ao_pins.h b/src/avr/ao_pins.h
new file mode 100644 (file)
index 0000000..bc423ff
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#ifdef AVR_DEMO
+       #define AO_LED_RED              (1<<7)
+       #define LEDS_AVAILABLE          (AO_LED_RED)
+       #define USE_SERIAL_1_STDIN      1
+       #define HAS_USB                 1
+       #define PACKET_HAS_SLAVE        0
+       #define HAS_SERIAL_1            1
+       #define TEENSY                  1
+       #define AVR_VCC_5V              1
+       #define AVR_VCC_3V3             0
+       #define AVR_CLOCK               16000000UL
+       #define HAS_BEEP                0
+#endif
+
+#ifdef TELESCIENCE
+       #define LEDS_AVAILABLE          0
+       #define HAS_USB                 1
+       #define HAS_LOG                 1
+       #define TEENSY                  0
+       #define HAS_SERIAL_1            0
+       #define HAS_ADC                 1
+       #define PACKET_HAS_SLAVE        0
+       #define HAS_BEEP                0
+       #define HAS_EEPROM              1
+       #define HAS_STORAGE_DEBUG       0
+
+       #define AVR_VCC_5V              0
+       #define AVR_VCC_3V3             1
+       #define AVR_CLOCK               8000000UL
+
+       #define SPI_CS_PORT             PORTE
+       #define SPI_CS_DIR              DDRE
+       #define M25_CS_MASK             (1 << PORTE6)
+       #define M25_MAX_CHIPS           1
+
+       #define SPI_SLAVE_CS_PORT       PORTB
+       #define SPI_SLAVE_CS_PIN        PINB
+       #define SPI_SLAVE_CS_PIN_NO     PINB0
+
+       #define SPI_SLAVE_PIN_0_3       1
+       #define SPI_SLAVE_PIN_2_5       0
+
+       #define IS_COMPANION            1
+#endif
+
+#ifdef TELEPYRO
+       #define LEDS_AVAILABLE          0
+       #define HAS_USB                 1
+       #define HAS_LOG                 0
+       #define TEENSY                  0
+       #define USE_SERIAL_1_STDIN      1
+       #define HAS_SERIAL_1            1
+       #define HAS_USB                 1
+       #define HAS_ADC                 1
+       #define PACKET_HAS_SLAVE        0
+       #define HAS_BEEP                0
+       #define HAS_EEPROM              1
+       #define USE_INTERNAL_FLASH      1
+       #define DISABLE_HELP            1
+       #define HAS_STORAGE_DEBUG       0
+       #define IS_COMPANION            1
+       #define HAS_ORIENT              0
+       #define ao_storage_pos_t        uint16_t
+
+       #define AVR_VCC_5V              0
+       #define AVR_VCC_3V3             1
+       #define AVR_CLOCK               8000000UL
+
+       #define SPI_SLAVE_CS_PORT       PORTB
+       #define SPI_SLAVE_CS_PIN        PINB
+       #define SPI_SLAVE_CS_PIN_NO     PINB0
+
+       #define SPI_SLAVE_PIN_0_3       1
+       #define SPI_SLAVE_PIN_2_5       0
+
+       #define AO_PYRO_NUM             8
+
+       #define AO_PYRO_PORT_0  B
+       #define AO_PYRO_PIN_0   5
+
+       #define AO_PYRO_PORT_1  B
+       #define AO_PYRO_PIN_1   6
+
+       #define AO_PYRO_PORT_2  B
+       #define AO_PYRO_PIN_2   7
+
+       #define AO_PYRO_PORT_3  C
+       #define AO_PYRO_PIN_3   6
+
+       #define AO_PYRO_PORT_4  C
+       #define AO_PYRO_PIN_4   7
+
+       #define AO_PYRO_PORT_5  D
+       #define AO_PYRO_PIN_5   5
+
+       #define AO_PYRO_PORT_6  D
+       #define AO_PYRO_PIN_6   3
+
+       #define AO_PYRO_PORT_7  D
+       #define AO_PYRO_PIN_7   2
+
+#endif
+
+#define AO_M25_SPI_CS_PORT     SPI_CS_PORT
+#define AO_M25_SPI_CS_MASK     M25_CS_MASK
+
+#define AO_TELESCIENCE_NUM_ADC 12
+
+struct ao_adc {
+       uint16_t        adc[AO_TELESCIENCE_NUM_ADC];    /* samples */
+};
+
+#define AO_DATA_RING   16
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/avr/ao_romconfig.c b/src/avr/ao_romconfig.c
new file mode 100644 (file)
index 0000000..ecc19c7
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+AO_ROMCONFIG_SYMBOL (0) uint16_t ao_serial_number = 0;
diff --git a/src/avr/ao_serial_avr.c b/src/avr/ao_serial_avr.c
new file mode 100644 (file)
index 0000000..dcee246
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+__xdata struct ao_fifo ao_serial1_rx_fifo;
+__xdata struct ao_fifo ao_serial1_tx_fifo;
+
+void
+ao_debug_out(char c)
+{
+       if (c == '\n')
+               ao_debug_out('\r');
+       loop_until_bit_is_set(UCSR1A, UDRE1);
+       UDR1 = c;
+}
+
+ISR(USART1_RX_vect)
+{
+       if (!ao_fifo_full(ao_serial1_rx_fifo))
+               ao_fifo_insert(ao_serial1_rx_fifo, UDR1);
+       ao_wakeup(&ao_serial1_rx_fifo);
+#if USE_SERIAL_1_STDIN
+       ao_wakeup(&ao_stdin_ready);
+#endif
+}
+
+static __xdata uint8_t ao_serial_tx1_started;
+
+static void
+ao_serial1_tx_start(void)
+{
+       if (!ao_fifo_empty(ao_serial1_tx_fifo) &&
+           !ao_serial_tx1_started)
+       {
+               ao_serial_tx1_started = 1;
+               ao_fifo_remove(ao_serial1_tx_fifo, UDR1);
+       }
+}
+
+ISR(USART1_UDRE_vect)
+{
+       ao_serial1_tx_started = 0;
+       ao_serial1_tx_start();
+       ao_wakeup(&ao_serial1_tx_fifo);
+}
+
+char
+ao_serial1_getchar(void) __critical
+{
+       char    c;
+       cli();
+       while (ao_fifo_empty(ao_serial1_rx_fifo))
+               ao_sleep(&ao_serial1_rx_fifo);
+       ao_fifo_remove(ao_serial1_rx_fifo, c);
+       sei();
+       return c;
+}
+
+#if USE_SERIAL_1_STDIN
+char
+ao_serial1_pollchar(void) __critical
+{
+       char    c;
+       cli();
+       if (ao_fifo_empty(ao_serial1_rx_fifo)) {
+               sei();
+               return AO_READ_AGAIN;
+       }
+       ao_fifo_remove(ao_serial1_rx_fifo,c);
+       sei();
+       return c;
+}
+#endif
+
+void
+ao_serial1_putchar(char c) __critical
+{
+       cli();
+       while (ao_fifo_full(ao_serial1_tx_fifo))
+               ao_sleep(&ao_serial1_tx_fifo);
+       ao_fifo_insert(ao_serial1_tx_fifo, c);
+       ao_serial_tx1_start();
+       sei();
+}
+
+void
+ao_serial1_drain(void) __critical
+{
+       cli();
+       while (!ao_fifo_empty(ao_serial1_tx_fifo))
+               ao_sleep(&ao_serial1_tx_fifo);
+       sei();
+}
+
+static const struct {
+       uint16_t ubrr;
+} ao_serial_speeds[] = {
+       /* [AO_SERIAL_SPEED_4800] = */ {
+               F_CPU / (16UL * 4800UL) - 1
+       },
+       /* [AO_SERIAL_SPEED_9600] = */ {
+               F_CPU / (16UL * 9600UL) - 1
+       },
+       /* [AO_SERIAL_SPEED_19200] = */ {
+               F_CPU / (16UL * 19200UL) - 1
+       },
+       /* [AO_SERIAL_SPEED_57600] = */ {
+               F_CPU / (16UL * 57600UL) - 1
+       },
+};
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+       ao_serial_drain();
+       if (speed > AO_SERIAL_SPEED_57600)
+               return;
+       UBRR1L = ao_serial_speeds[speed].ubrr;
+       UBRR1H = ao_serial_speeds[speed].ubrr >> 8;
+}
+
+void
+ao_serial_init(void)
+{
+       /* Ensure the uart is powered up */
+
+       PRR1 &= ~(1 << PRUSART1);
+
+       /* Pick a 9600 baud rate */
+       ao_serial_set_speed(AO_SERIAL_SPEED_9600);
+
+       UCSR1A = 0;
+       UCSR1C = ((0 << UMSEL10) |      /* Asynchronous mode */
+                 (0 << UPM10) |        /* No parity */
+                 (0 << USBS1) |        /* 1 stop bit */
+                 (3 << UCSZ10) |       /* 8 bit characters */
+                 (0 << UCPOL1));       /* MBZ for async mode */
+       UCSR1B = ((1 << RXEN1) |        /* Enable receiver */
+                 (1 << TXEN1) |        /* Enable transmitter */
+                 (1 << RXCIE1) |       /* Enable receive interrupts */
+                 (1 << UDRIE1));       /* Enable transmit empty interrupts */
+#if USE_SERIAL_1_STDIN
+       ao_add_stdio(ao_serial1_pollchar,
+                    ao_serial1_putchar,
+                    NULL);
+#endif
+}
diff --git a/src/avr/ao_spi_slave.c b/src/avr/ao_spi_slave.c
new file mode 100644 (file)
index 0000000..b742d29
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+uint8_t
+ao_spi_slave_recv(uint8_t *buf, uint8_t len)
+{
+       while (len--) {
+               while (!(SPSR & (1 << SPIF)))
+                       if ((PINB & (1 << PINB0)))
+                               return 0;
+               *buf++ = SPDR;
+       }
+       return 1;
+}
+
+void
+ao_spi_slave_send(uint8_t *buf, uint8_t len)
+{
+       while (len--) {
+               SPDR = *buf++;
+               while (!(SPSR & (1 << SPIF)))
+                       if ((PINB & (1 << PINB0)))
+                               return;
+       }
+       /* Clear pending SPIF bit by reading */
+       (void) SPDR;
+}
+
+static uint8_t ao_spi_slave_running;
+
+ISR(PCINT0_vect)
+{
+       cli();
+#if SPI_SLAVE_PIN_0_3
+       if ((PINB & (1 << PORTB0)) == 0)
+#endif
+#if SPI_SLAVE_PIN_2_5
+       if ((PINB & (1 << PORTB2)) == 0)
+#endif
+       {
+               if (!ao_spi_slave_running) {
+                       ao_spi_slave_running = 1;
+                       ao_spi_slave();
+               }
+       } else {
+               ao_spi_slave_running = 0;
+       }
+       sei();
+}
+
+void
+ao_spi_slave_init(void)
+{
+       /* We'd like to have a pull-up on SS so that disconnecting the
+        * TM would cause any SPI transaction to abort. However, when
+        * I tried that, SPI transactions would spontaneously abort,
+        * making me assume that we needed a less aggressive pull-up
+        * than is offered inside the AVR
+        */
+#if SPI_SLAVE_PIN_0_3
+       PCMSK0 |= (1 << PCINT0);        /* Enable PCINT0 pin change */
+       PCICR |= (1 << PCIE0);          /* Enable pin change interrupt */
+
+       DDRB = ((DDRB & 0xf0) |
+               (1 << 3) |              /* MISO, output */
+               (0 << 2) |              /* MOSI, input */
+               (0 << 1) |              /* SCK, input */
+               (0 << 0));              /* SS, input */
+
+       PORTB = ((PORTB & 0xf0) |
+                (1 << 3) |             /* MISO, output */
+                (0 << 2) |             /* MOSI, no pull-up */
+                (0 << 1) |             /* SCK, no pull-up */
+                (1 << 0));             /* SS, pull-up */
+#endif
+#if SPI_SLAVE_PIN_2_5
+       PCMSK0 |= (1 << PCINT2);        /* Enable PCINT2 pin change */
+       PCICR |= (1 << PCIE0);          /* Enable pin change interrupt */
+
+       DDRB = ((DDRB & 0xf0) |
+               (0 << 5) |              /* SCK, input */
+               (1 << 4) |              /* MISO, output */
+               (0 << 3) |              /* MOSI, input */
+               (0 << 2));              /* SS, input */
+
+       PORTB = ((PORTB & 0xf0) |
+                (0 << 5) |             /* SCK, no pull-up */
+                (1 << 4) |             /* MISO, output */
+                (0 << 3) |             /* MOSI, no pull-up */
+                (1 << 2));             /* SS, pull-up */
+#endif 
+
+       SPCR = (0 << SPIE) |            /* Disable SPI interrupts */
+               (1 << SPE) |            /* Enable SPI */
+               (0 << DORD) |           /* MSB first */
+               (0 << MSTR) |           /* Slave mode */
+               (0 << CPOL) |           /* Clock low when idle */
+               (0 << CPHA);            /* Sample at leading clock edge */
+}
diff --git a/src/avr/ao_spi_usart.c b/src/avr/ao_spi_usart.c
new file mode 100644 (file)
index 0000000..7c41042
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/*
+ * Atmega32u4 USART in MSPIM (master SPI mode)
+ */
+
+__xdata uint8_t        ao_spi_mutex;
+
+/* Send bytes over SPI.
+ *
+ * This just polls; the SPI is set to go as fast as possible,
+ * so using interrupts would take way too long
+ */
+void
+ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant
+{
+       uint8_t *d = block;
+
+       while (len--) {
+               while (!(UCSR1A & (1 << UDRE1)));
+               UDR1 = *d++;
+               while (!(UCSR1A & (1 << RXC1)));
+               (void) UDR1;
+       }
+}
+
+/* Receive bytes over SPI.
+ *
+ * Poll, sending zeros and reading data back
+ */
+void
+ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant
+{
+       uint8_t *d = block;
+
+       /* Clear any pending data */
+       while (UCSR1A & (1 << RXC1))
+               (void) UDR1;
+       
+       while (len--) {
+               while (!(UCSR1A & (1 << UDRE1)));
+               UDR1 = 0;
+               while (!(UCSR1A & (1 << RXC1)));
+               *d++ = UDR1;
+       }
+}
+
+/*
+ * Initialize USART0 for SPI using config alt 2
+ *
+ *     MO      P1_5
+ *     MI      P1_4
+ *     CLK     P1_3
+ *
+ * Chip select is the responsibility of the caller
+ */
+
+#define XCK1_DDR       DDRD
+#define XCK1_PORT      PORTD
+#define XCK1           PORTD5
+#define XMS1_DDR       DDRE
+#define XMS1_PORT      PORTE
+#define XMS1           PORTE6
+
+void
+ao_spi_init(void)
+{
+       /* Ensure the USART is powered */
+       PRR1 &= ~(1 << PRUSART1);
+
+       /*
+        * Set pin directions
+        */
+       XCK1_DDR |= (1 << XCK1);
+
+       /* Clear chip select (which is negated) */
+       XMS1_PORT |= (1 < XMS1);
+       XMS1_DDR |= (1 << XMS1);
+
+       /* Set baud register to zero (required before turning transmitter on) */
+       UBRR1 = 0;
+
+       UCSR1C = ((0x3 << UMSEL10) |    /* Master SPI mode */
+                 (0 << UCSZ10) |       /* SPI mode 0 */
+                 (0 << UCPOL1));       /* SPI mode 0 */
+
+       /* Enable transmitter and receiver */
+       UCSR1B = ((1 << RXEN1) |
+                 (1 << TXEN1));
+
+       /* It says that 0 is a legal value; we'll see... */
+       UBRR1 = 0;
+}
diff --git a/src/avr/ao_timer.c b/src/avr/ao_timer.c
new file mode 100644 (file)
index 0000000..d2ea2be
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+volatile __data uint16_t ao_tick_count;
+
+uint16_t ao_time(void)
+{
+       uint16_t        v;
+       ao_arch_critical(
+               v = ao_tick_count;
+               );
+       return v;
+}
+
+#define T1_CLOCK_DIVISOR       8       /* 24e6/8 = 3e6 */
+#define T1_SAMPLE_TIME         30000   /* 3e6/30000 = 100 */
+
+#if HAS_ADC
+volatile __data uint8_t        ao_adc_interval = 1;
+volatile __data uint8_t        ao_adc_count;
+#endif
+
+void
+ao_debug_out(char c);
+
+ISR(TIMER1_COMPA_vect)
+{
+       ++ao_tick_count;
+#if HAS_ADC
+       if (++ao_adc_count == ao_adc_interval) {
+               ao_adc_count = 0;
+               ao_adc_poll();
+       }
+#endif
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval) __critical
+{
+       ao_adc_interval = interval;
+       ao_adc_count = 0;
+}
+#endif
+
+void
+ao_timer_init(void)
+{
+       TCCR1A = ((0 << WGM11) |        /* CTC mode, OCR1A */
+                 (0 << WGM10));        /* CTC mode, OCR1A */
+       TCCR1B = ((0 << ICNC1) |        /* no input capture noise canceler */
+                 (0 << ICES1) |        /* input capture on falling edge (don't care) */
+                 (0 << WGM13) |        /* CTC mode, OCR1A */
+                 (1 << WGM12) |        /* CTC mode, OCR1A */
+                 (3 << CS10));         /* clk/64 from prescaler */
+
+#if TEENSY
+       OCR1A = 2500;                   /* 16MHz clock */
+#else
+       OCR1A = 1250;                   /* 8MHz clock */
+#endif
+
+       TIMSK1 = (1 << OCIE1A);         /* Interrupt on compare match */
+}
diff --git a/src/avr/ao_usb_avr.c b/src/avr/ao_usb_avr.c
new file mode 100644 (file)
index 0000000..9ba407a
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_usb.h"
+
+#define USB_DEBUG 0
+
+#if USB_DEBUG
+#define debug(format, args...) printf(format, ## args)
+#else
+#define debug(format, args...)
+#endif
+
+struct ao_task __xdata ao_usb_task;
+
+struct ao_usb_setup {
+       uint8_t         dir_type_recip;
+       uint8_t         request;
+       uint16_t        value;
+       uint16_t        index;
+       uint16_t        length;
+} __xdata ao_usb_setup;
+
+static __xdata uint8_t         ao_usb_ep0_state;
+static const uint8_t * __xdata ao_usb_ep0_in_data;
+static __xdata uint8_t         ao_usb_ep0_in_len;
+static __xdata uint8_t ao_usb_ep0_in_pending;
+static __xdata uint8_t ao_usb_addr_pending;
+static __xdata uint8_t ao_usb_ep0_in_buf[2];
+static __xdata uint8_t         ao_usb_ep0_out_len;
+static __xdata uint8_t *__xdata ao_usb_ep0_out_data;
+
+static __xdata uint8_t ao_usb_in_flushed;
+static __xdata uint8_t ao_usb_running;
+static __xdata uint8_t ao_usb_configuration;
+static __xdata uint8_t ueienx_0;
+
+void
+ao_usb_set_address(uint8_t address)
+{
+       UDADDR = (0 << ADDEN) | address;
+       ao_usb_addr_pending = 1;
+}
+
+#define EP_SIZE(s)     ((s) == 64 ? 0x30 :     \
+                       ((s) == 32 ? 0x20 :     \
+                       ((s) == 16 ? 0x10 :     \
+                                    0x00)))
+
+static void
+ao_usb_dump_ep(uint8_t ep)
+{
+       UENUM = ep;
+       debug ("EP %d: UECONX %02x UECFG0X %02x UECFG1X %02x UEIENX %02x UESTA0X %02x UESTA1X %02X\n",
+               ep, UECONX, UECFG0X, UECFG1X, UEIENX, UESTA0X, UESTA1X);
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+       debug ("set_ep0\n");
+       /* Set the CONTROL max packet size, single buffered */
+       UENUM = 0;
+       UECONX = (1 << EPEN);                                   /* Enable */
+
+       UECFG0X = ((0 << EPTYPE0) |                             /* Control */
+                  (0 << EPDIR));                               /* Out (ish) */
+
+       UECFG1X = (EP_SIZE(AO_USB_CONTROL_SIZE) |               /* Size */
+                  (0 << EPBK0) |                               /* Single bank */
+                  (1 << ALLOC));
+
+       ueienx_0 = ((1 << RXSTPE) |                             /* Enable SETUP interrupt */
+                   (1 << RXOUTE));                             /* Enable OUT interrupt */
+
+//     ao_usb_dump_ep(0);
+       ao_usb_addr_pending = 0;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+       /* Set the IN max packet size, double buffered */
+       UENUM = AO_USB_IN_EP;
+       UECONX = (1 << EPEN);                                   /* Enable */
+
+       UECFG0X = ((2 << EPTYPE0) |                             /* Bulk */
+                  (1 << EPDIR));                               /* In */
+
+       UECFG1X = (EP_SIZE(AO_USB_IN_SIZE) |                    /* Size */
+                  (1 << EPBK0) |                               /* Double bank */
+                  (1 << ALLOC));                               /* Allocate */
+
+#if 0
+       UEIENX = ((1 << TXINE));                                /* Enable IN complete interrupt */
+#endif
+
+       ao_usb_dump_ep(AO_USB_IN_EP);
+
+       /* Set the OUT max packet size, double buffered */
+       UENUM = AO_USB_OUT_EP;
+       UECONX |= (1 << EPEN);                                  /* Enable */
+
+       UECFG0X = ((2 << EPTYPE0) |                             /* Bulk */
+                  (0 << EPDIR));                               /* Out */
+
+       UECFG1X = (EP_SIZE(AO_USB_OUT_SIZE) |                   /* Size */
+                  (1 << EPBK0) |                               /* Double bank */
+                  (1 << ALLOC));                               /* Allocate */
+
+       UEIENX = ((1 << RXOUTE));                               /* Enable OUT complete interrupt */
+
+       ao_usb_dump_ep(AO_USB_OUT_EP);
+       ao_usb_running = 1;
+}
+
+ISR(USB_GEN_vect)
+{
+       ao_wakeup(&ao_usb_task);
+}
+
+
+__xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value)
+{
+       const uint8_t           *__xdata descriptor;
+       __xdata uint8_t         type = value >> 8;
+       __xdata uint8_t         index = value;
+
+       descriptor = ao_usb_descriptors;
+       while (descriptor[0] != 0) {
+               if (descriptor[1] == type && index-- == 0) {
+                       if (type == AO_USB_DESC_CONFIGURATION)
+                               ao_usb_ep0_in_len = descriptor[2];
+                       else
+                               ao_usb_ep0_in_len = descriptor[0];
+                       ao_usb_ep0_in_data = descriptor;
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+static void
+ao_usb_ep0_set_in_pending(uint8_t in_pending)
+{
+       ao_usb_ep0_in_pending = in_pending;
+
+       if (in_pending)
+               ueienx_0 = ((1 << RXSTPE) | (1 << RXOUTE) | (1 << TXINE));      /* Enable IN interrupt */
+}
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+       __xdata uint8_t this_len;
+
+       cli();
+       UENUM = 0;
+       if (!(UEINTX & (1 << TXINI))) {
+               debug("EP0 not accepting IN data\n");
+               ao_usb_ep0_set_in_pending(1);
+       } else {
+               this_len = ao_usb_ep0_in_len;
+               if (this_len > AO_USB_CONTROL_SIZE)
+                       this_len = AO_USB_CONTROL_SIZE;
+
+               ao_usb_ep0_in_len -= this_len;
+
+               /* Set IN interrupt enable */
+               if (ao_usb_ep0_in_len == 0 && this_len != AO_USB_CONTROL_SIZE)
+                       ao_usb_ep0_set_in_pending(0);
+               else
+                       ao_usb_ep0_set_in_pending(1);
+
+               debug ("Flush EP0 len %d:", this_len);
+               while (this_len--) {
+                       uint8_t c = *ao_usb_ep0_in_data++;
+                       debug(" %02x", c);
+                       UEDATX = c;
+               }
+               debug ("\n");
+
+               /* Clear the TXINI bit to send the packet */
+               UEINTX &= ~(1 << TXINI);
+       }
+       sei();
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(uint8_t len, uint8_t ack)
+{
+       if (len > ao_usb_ep0_out_len)
+               len = ao_usb_ep0_out_len;
+       ao_usb_ep0_out_len -= len;
+
+//     debug ("EP0 UEINTX %02x UEBCLX %d UEBCHX %d\n",
+//             UEINTX, UEBCLX, UEBCHX);
+       /* Pull all of the data out of the packet */
+       debug ("Fill EP0 len %d:", len);
+       UENUM = 0;
+       while (len--) {
+               uint8_t c = UEDATX;
+               *ao_usb_ep0_out_data++ = c;
+               debug (" %02x", c);
+       }
+       debug ("\n");
+
+       /* ACK the packet */
+       UEINTX &= ~ack;
+}
+
+void
+ao_usb_ep0_queue_byte(uint8_t a)
+{
+       ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       /* Pull the setup packet out of the fifo */
+       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
+       ao_usb_ep0_out_len = 8;
+       ao_usb_ep0_fill(8, (1 << RXSTPI) | (1 << RXOUTI) | (1 << TXINI));
+       if (ao_usb_ep0_out_len != 0) {
+               debug ("invalid setup packet length\n");
+               return;
+       }
+
+       /* Figure out how to ACK the setup packet */
+       if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
+               if (ao_usb_setup.length)
+                       ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+               else
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       } else {
+               if (ao_usb_setup.length)
+                       ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+               else
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       }
+/*
+       UENUM = 0;
+       if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
+       else
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY;
+*/
+
+       ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+       ao_usb_ep0_in_len = 0;
+       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+       case AO_USB_TYPE_STANDARD:
+               debug ("Standard setup packet\n");
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       debug ("Device setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               debug ("get status\n");
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               debug ("set address %d\n", ao_usb_setup.value);
+                               ao_usb_set_address(ao_usb_setup.value);
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               debug ("get descriptor %d\n", ao_usb_setup.value);
+                               ao_usb_get_descriptor(ao_usb_setup.value);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               debug ("get configuration %d\n", ao_usb_configuration);
+                               ao_usb_ep0_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = ao_usb_setup.value;
+                               debug ("set configuration %d\n", ao_usb_configuration);
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       debug ("Interface setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_GET_INTERFACE:
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_INTERFACE:
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_ENDPOINT:
+                       debug ("Endpoint setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       }
+                       break;
+               }
+               break;
+       case AO_USB_TYPE_CLASS:
+               debug ("Class setup packet\n");
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       debug ("set line coding\n");
+                       ao_usb_ep0_out_len = 7;
+                       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       debug ("get line coding\n");
+                       ao_usb_ep0_in_len = 7;
+                       ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
+                       break;
+               case AO_USB_SET_CONTROL_LINE_STATE:
+                       break;
+               }
+               break;
+       }
+       if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
+               if (ao_usb_setup.length < ao_usb_ep0_in_len)
+                       ao_usb_ep0_in_len = ao_usb_setup.length;
+               debug ("Start ep0 in delivery %d\n", ao_usb_ep0_in_len);
+               ao_usb_ep0_set_in_pending(1);
+       }
+}
+
+/* End point 0 receives all of the control messages. */
+static void
+ao_usb_ep0(void)
+{
+       uint8_t intx, udint;
+
+       debug ("usb task started\n");
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       for (;;) {
+               cli();
+               for (;;) {
+                       udint = UDINT;
+                       UDINT = 0;
+//                     debug ("UDINT %02x\n", udint);
+                       if (udint & (1 << EORSTI)) {
+                               ao_usb_configuration = 0;
+                               ao_usb_set_ep0();
+                       }
+                       UENUM = 0;
+                       intx = UEINTX;
+//                     debug ("UEINTX %02x\n", intx);
+                       if (intx & ((1 << RXSTPI) | (1 << RXOUTI)))
+                               break;
+                       if ((intx & (1 << TXINI))) {
+                               if (ao_usb_ep0_in_pending)
+                                       break;
+                               else
+                               {
+                                       if (ao_usb_addr_pending) {
+                                               UDADDR |= (1 << ADDEN);
+                                               ao_usb_addr_pending = 0;
+                                       }
+                                       ueienx_0 = ((1 << RXSTPE) | (1 << RXOUTE));     /* Disable IN interrupt */
+                               }
+                       }
+//                     debug ("usb task sleeping...\n");
+                       UENUM = 0;
+                       UEIENX = ueienx_0;
+                       ao_sleep(&ao_usb_task);
+               }
+               sei();
+//             debug ("UEINTX for ep0 is %02x\n", intx);
+               if (intx & (1 << RXSTPI)) {
+                       ao_usb_ep0_setup();
+               }
+               if (intx & (1 << RXOUTI)) {
+                       ao_usb_ep0_fill(UEBCLX, (1 << RXOUTI));
+                       ao_usb_ep0_set_in_pending(1);
+               }
+               if (intx & (1 << TXINI) && ao_usb_ep0_in_pending) {
+                       debug ("continue sending ep0 IN data\n");
+                       ao_usb_ep0_flush();
+               }
+       }
+}
+
+/* Wait for a free IN buffer */
+static void
+ao_usb_in_wait(void)
+{
+       for (;;) {
+               /* Check if the current buffer is writable */
+               UENUM = AO_USB_IN_EP;
+               if (UEINTX & (1 << RWAL))
+                       break;
+
+               cli();
+               /* Wait for an IN buffer to be ready */
+               for (;;) {
+                       UENUM = AO_USB_IN_EP;
+                       if ((UEINTX & (1 << TXINI)))
+                               break;
+                       UEIENX = (1 << TXINE);
+                       ao_sleep(&ao_usb_in_flushed);
+               }
+               /* Ack the interrupt */
+               UEINTX &= ~(1 << TXINI);
+               sei();
+       }
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+ao_usb_in_send(void)
+{
+       UENUM = AO_USB_IN_EP;
+       UEINTX &= ~(1 << FIFOCON);
+}
+
+void
+ao_usb_flush(void) __critical
+{
+       if (!ao_usb_running)
+               return;
+
+       /* Anytime we've sent a character since
+        * the last time we flushed, we'll need
+        * to send a packet -- the only other time
+        * we would send a packet is when that
+        * packet was full, in which case we now
+        * want to send an empty packet
+        */
+       if (!ao_usb_in_flushed) {
+               ao_usb_in_flushed = 1;
+               ao_usb_in_wait();
+               ao_usb_in_send();
+       }
+}
+
+void
+ao_usb_putchar(char c) __critical __reentrant
+{
+       if (!ao_usb_running)
+               return;
+
+       ao_usb_in_wait();
+
+       /* Queue a byte */
+       UENUM = AO_USB_IN_EP;
+       UEDATX = c;
+
+       /* Send the packet when full */
+       if ((UEINTX & (1 << RWAL)) == 0)
+               ao_usb_in_send();
+       ao_usb_in_flushed = 0;
+}
+
+static char
+_ao_usb_pollchar(void)
+{
+       char c;
+       uint8_t intx;
+
+       if (!ao_usb_running)
+               return AO_READ_AGAIN;
+
+       for (;;) {
+               UENUM = AO_USB_OUT_EP;
+               intx = UEINTX;
+               debug("usb_pollchar UEINTX %02d\n", intx);
+               if (intx & (1 << RWAL))
+                       break;
+
+               if (intx & (1 << FIFOCON)) {
+                       /* Ack the last packet */
+                       UEINTX = (uint8_t) ~(1 << FIFOCON);
+               }
+
+               /* Check to see if a packet has arrived */
+               if ((intx & (1 << RXOUTI)) == 0) {
+                       UENUM = AO_USB_OUT_EP;
+                       UEIENX = (1 << RXOUTE);
+                       return AO_READ_AGAIN;
+               }
+
+               /* Ack the interrupt */
+               UEINTX = ~(1 << RXOUTI);
+       }
+
+       /* Pull a character out of the fifo */
+       c = UEDATX;
+       return c;
+}
+
+char
+ao_usb_pollchar(void)
+{
+       char    c;
+       cli();
+       c = _ao_usb_pollchar();
+       sei();
+       return c;
+}
+
+char
+ao_usb_getchar(void) __critical
+{
+       char    c;
+
+       cli();
+       while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(&ao_stdin_ready);
+       sei();
+       return c;
+}
+
+uint16_t       control_count;
+uint16_t       in_count;
+uint16_t       out_count;
+
+/* Endpoint interrupt */
+ISR(USB_COM_vect)
+{
+       uint8_t old_num = UENUM;
+       uint8_t i = UEINT;
+
+#ifdef AO_LED_RED
+       ao_led_toggle(AO_LED_RED);
+#endif
+       UEINT = 0;
+       if (i & (1 << 0)) {
+               UENUM = 0;
+               UEIENX = 0;
+               ao_wakeup(&ao_usb_task);
+               ++control_count;
+       }
+       if (i & (1 << AO_USB_IN_EP)) {
+               UENUM = AO_USB_IN_EP;
+               UEIENX = 0;
+               ao_wakeup(&ao_usb_in_flushed);
+               in_count++;
+       }
+       if (i & (1 << AO_USB_OUT_EP)) {
+               UENUM = AO_USB_OUT_EP;
+               UEIENX = 0;
+               ao_wakeup(&ao_stdin_ready);
+               ++out_count;
+       }
+       UENUM = old_num;
+}
+
+#if AVR_VCC_5V
+#define AO_PAD_REGULATOR_INIT  (1 << UVREGE)   /* Turn on pad regulator */
+#endif
+#if AVR_VCC_3V3
+/* TeleScience V0.1 has a hardware bug -- UVcc is hooked up, but UCap is not
+ * Make this work by running power through UVcc to the USB system
+ */
+#define AO_PAD_REGULATOR_INIT  (1 << UVREGE)   /* Turn off pad regulator */
+#endif
+
+#if AVR_CLOCK == 16000000UL
+#define AO_USB_PLL_INPUT_PRESCALER     (1 << PINDIV)   /* Divide 16MHz clock by 2 */
+#endif
+#if AVR_CLOCK == 8000000UL
+#define AO_USB_PLL_INPUT_PRESCALER     0               /* Don't divide clock */
+#endif
+
+void
+ao_usb_disable(void)
+{
+       /* Unplug from the bus */
+       UDCON = (1 << DETACH);
+
+       /* Disable the interface */
+       USBCON = 0;
+
+       /* Disable the PLL */
+       PLLCSR = 0;
+
+       /* Turn off the pad regulator */
+       UHWCON = 0;
+}
+
+#define AO_USB_CON ((1 << USBE) |      /* USB enable */ \
+                   (0 << RSTCPU) |     /* do not reset CPU */  \
+                   (0 << LSM) |        /* Full speed mode */   \
+                   (0 << RMWKUP))      /* no remote wake-up */ \
+
+void
+ao_usb_enable(void)
+{
+       /* Configure pad regulator */
+       UHWCON = AO_PAD_REGULATOR_INIT;
+
+       /* Enable USB device, but freeze the clocks until initialized */
+       USBCON = AO_USB_CON | (1 <<FRZCLK);
+
+       /* Enable PLL with appropriate divider */
+       PLLCSR = AO_USB_PLL_INPUT_PRESCALER | (1 << PLLE);
+
+       /* Wait for PLL to lock */
+       loop_until_bit_is_set(PLLCSR, (1 << PLOCK));
+
+       /* Enable USB, enable the VBUS pad */
+       USBCON = AO_USB_CON | (1 << OTGPADE);
+
+       /* Enable global interrupts */
+       UDIEN = (1 << EORSTE);          /* End of reset interrupt */
+
+       ao_usb_configuration = 0;
+
+       debug ("ao_usb_enable\n");
+
+       debug ("UHWCON %02x USBCON %02x PLLCSR %02x UDIEN %02x\n",
+              UHWCON, USBCON, PLLCSR, UDIEN);
+       UDCON = (0 << DETACH);  /* Clear the DETACH bit to plug into the bus */
+}
+
+#if USB_DEBUG
+struct ao_task __xdata ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+       char    c;
+
+       for (;;) {
+               c = ao_usb_getchar();
+               ao_usb_putchar(c);
+               ao_usb_flush();
+       }
+}
+#endif
+
+void
+ao_usb_init(void)
+{
+       ao_usb_enable();
+
+       debug ("ao_usb_init\n");
+       ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");
+#if USB_DEBUG
+       ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+       ao_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+}
diff --git a/src/cc1111.h b/src/cc1111.h
deleted file mode 100644 (file)
index 87b1448..0000000
+++ /dev/null
@@ -1,1240 +0,0 @@
-/*-------------------------------------------------------------------------
-   Register Declarations for the ChipCon CC1111 Processor Range
-
-   Copyright © 2008 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.
-
-   Adapted from the Cygnal C8051F12x config file which is:
-
-   Copyright (C) 2003 - Maarten Brock, sourceforge.brock@dse.nl
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library 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
-   Lesser General Public License for more details.
-
-   You should have received a copy of the GNU Lesser General Public
-   License along with this library; if not, write to the Free Software
-   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
--------------------------------------------------------------------------*/
-
-#ifndef _CC1111_H_
-#define _CC1111_H_
-#include <cc1110.h>
-#include <stdint.h>
-
-sfr at 0xA8 IEN0;              /* Interrupt Enable 0 Register */
-
-sbit at 0xA8 RFTXRXIE;         /* RF TX/RX done interrupt enable */
-sbit at 0xA9 ADCIE;            /* ADC interrupt enable */
-sbit at 0xAA URX0IE;           /* USART0 RX interrupt enable */
-sbit at 0xAB URX1IE;           /* USART1 RX interrupt enable (shared with I2S RX) */
-sbit at 0xAB I2SRXIE;          /* I2S RX interrupt enable (shared with USART1 RX) */
-sbit at 0xAC ENCIE;            /* AES encryption/decryption interrupt enable */
-sbit at 0xAD STIE;             /* Sleep Timer interrupt enable */
-sbit at 0xAF EA;               /* Enable All */
-
-#define IEN0_EA                        (1 << 7)
-#define IEN0_STIE              (1 << 5)
-#define IEN0_ENCIE             (1 << 4)
-#define IEN0_URX1IE            (1 << 3)
-#define IEN0_I2SRXIE           (1 << 3)
-#define IEN0_URX0IE            (1 << 2)
-#define IEN0_ADCIE             (1 << 1)
-#define IEN0_RFTXRXIE          (1 << 0)
-
-sfr at 0xB8 IEN1;              /* Interrupt Enable 1 Register */
-
-#define IEN1_P0IE              (1 << 5)        /* Port 0 interrupt enable */
-#define IEN1_T4IE              (1 << 4)        /* Timer 4 interrupt enable */
-#define IEN1_T3IE              (1 << 3)        /* Timer 3 interrupt enable */
-#define IEN1_T2IE              (1 << 2)        /* Timer 2 interrupt enable */
-#define IEN1_T1IE              (1 << 1)        /* Timer 1 interrupt enable */
-#define IEN1_DMAIE             (1 << 0)        /* DMA transfer interrupt enable */
-
-/* IEN2 */
-sfr at 0x9A IEN2;              /* Interrupt Enable 2 Register */
-
-#define IEN2_WDTIE             (1 << 5)        /* Watchdog timer interrupt enable */
-#define IEN2_P1IE              (1 << 4)        /* Port 1 interrupt enable */
-#define IEN2_UTX1IE            (1 << 3)        /* USART1 TX interrupt enable */
-#define IEN2_I2STXIE           (1 << 3)        /* I2S TX interrupt enable */
-#define IEN2_UTX0IE            (1 << 2)        /* USART0 TX interrupt enable */
-#define IEN2_P2IE              (1 << 1)        /* Port 2 interrupt enable */
-#define IEN2_USBIE             (1 << 1)        /* USB interrupt enable */
-#define IEN2_RFIE              (1 << 0)        /* RF general interrupt enable */
-
-/* CLKCON 0xC6 */
-sfr at 0xC6 CLKCON;            /* Clock Control */
-
-#define CLKCON_OSC32K_RC       (1 << 7)
-#define CLKCON_OSC32K_XTAL     (0 << 7)
-#define CLKCON_OSC32K_MASK     (1 << 7)
-#define CLKCON_OSC_RC          (1 << 6)
-#define CLKCON_OSC_XTAL                (0 << 6)
-#define CLKCON_OSC_MASK                (1 << 6)
-#define CLKCON_TICKSPD_MASK    (7 << 3)
-# define CLKCON_TICKSPD_1      (0 << 3)
-# define CLKCON_TICKSPD_1_2    (1 << 3)
-# define CLKCON_TICKSPD_1_4    (2 << 3)
-# define CLKCON_TICKSPD_1_8    (3 << 3)
-# define CLKCON_TICKSPD_1_16   (4 << 3)
-# define CLKCON_TICKSPD_1_32   (5 << 3)
-# define CLKCON_TICKSPD_1_64   (6 << 3)
-# define CLKCON_TICKSPD_1_128  (7 << 3)
-
-#define CLKCON_CLKSPD_MASK     (7 << 0)
-# define CLKCON_CLKSPD_1       (0 << 0)
-# define CLKCON_CLKSPD_1_2     (1 << 0)
-# define CLKCON_CLKSPD_1_4     (2 << 0)
-# define CLKCON_CLKSPD_1_8     (3 << 0)
-# define CLKCON_CLKSPD_1_16    (4 << 0)
-# define CLKCON_CLKSPD_1_32    (5 << 0)
-# define CLKCON_CLKSPD_1_64    (6 << 0)
-# define CLKCON_CLKSPD_1_128   (7 << 0)
-
-/* SLEEP 0xBE */
-#define SLEEP_USB_EN           (1 << 7)
-#define SLEEP_XOSC_STB         (1 << 6)
-#define SLEEP_HFRC_STB         (1 << 5)
-#define SLEEP_RST_POWER                (0 << 3)
-#define SLEEP_RST_EXTERNAL     (1 << 3)
-#define SLEEP_RST_WATCHDOG     (2 << 3)
-#define SLEEP_RST_MASK         (3 << 3)
-#define SLEEP_OSC_PD           (1 << 2)
-#define SLEEP_MODE_PM0         (0 << 0)
-#define SLEEP_MODE_PM1         (1 << 0)
-#define SLEEP_MODE_PM2         (2 << 0)
-#define SLEEP_MODE_PM3         (3 << 0)
-#define SLEEP_MODE_MASK                (3 << 0)
-
-/* PCON 0x87 */
-sfr at 0x87 PCON;              /* Power Mode Control Register */
-
-#define PCON_IDLE              (1 << 0)
-
-/*
- * TCON
- */
-sfr at 0x88 TCON;      /* CPU Interrupt Flag 1 */
-
-sbit at 0x8F URX1IF;   /* USART1 RX interrupt flag. Automatically cleared */
-sbit at 0x8F I2SRXIF;  /* I2S RX interrupt flag. Automatically cleared */
-sbit at 0x8D ADCIF;    /* ADC interrupt flag. Automatically cleared */
-sbit at 0x8B URX0IF;   /* USART0 RX interrupt flag. Automatically cleared */
-sbit at 0x89 RFTXRXIF; /* RF TX/RX complete interrupt flag. Automatically cleared */
-
-#define TCON_URX1IF    (1 << 7)
-#define TCON_I2SRXIF   (1 << 7)
-#define TCON_ADCIF     (1 << 5)
-#define TCON_URX0IF    (1 << 3)
-#define TCON_RFTXRXIF  (1 << 1)
-
-/*
- * S0CON
- */
-sfr at 0x98 S0CON;     /* CPU Interrupt Flag 2 */
-
-sbit at 0x98 ENCIF_0;  /* AES interrupt 0. */
-sbit at 0x99 ENCIF_1;  /* AES interrupt 1. */
-
-#define S0CON_ENCIF_1  (1 << 1)
-#define S0CON_ENCIF_0  (1 << 0)
-
-/*
- * S1CON
- */
-sfr at 0x9B S1CON;     /* CPU Interrupt Flag 3 */
-
-#define S1CON_RFIF_1   (1 << 1)
-#define S1CON_RFIF_0   (1 << 0)
-
-/*
- * IRCON
- */
-sfr at 0xC0 IRCON;     /* CPU Interrupt Flag 4 */
-
-sbit at 0xC0 DMAIF;    /* DMA complete interrupt flag */
-sbit at 0xC1 T1IF;     /* Timer 1 interrupt flag. Automatically cleared */
-sbit at 0xC2 T2IF;     /* Timer 2 interrupt flag. Automatically cleared */
-sbit at 0xC3 T3IF;     /* Timer 3 interrupt flag. Automatically cleared */
-sbit at 0xC4 T4IF;     /* Timer 4 interrupt flag. Automatically cleared */
-sbit at 0xC5 P0IF;     /* Port0 interrupt flag */
-sbit at 0xC7 STIF;     /* Sleep Timer interrupt flag */
-
-#define IRCON_DMAIF    (1 << 0)        /* DMA complete interrupt flag */
-#define IRCON_T1IF     (1 << 1)        /* Timer 1 interrupt flag. Automatically cleared */
-#define IRCON_T2IF     (1 << 2)        /* Timer 2 interrupt flag. Automatically cleared */
-#define IRCON_T3IF     (1 << 3)        /* Timer 3 interrupt flag. Automatically cleared */
-#define IRCON_T4IF     (1 << 4)        /* Timer 4 interrupt flag. Automatically cleared */
-#define IRCON_P0IF     (1 << 5)        /* Port0 interrupt flag */
-#define IRCON_STIF     (1 << 7)        /* Sleep Timer interrupt flag */
-
-/*
- * IRCON2
- */
-sfr at 0xE8 IRCON2;    /* CPU Interrupt Flag 5 */
-
-sbit at 0xE8 USBIF;    /* USB interrupt flag (shared with Port2) */
-sbit at 0xE8 P2IF;     /* Port2 interrupt flag (shared with USB) */
-sbit at 0xE9 UTX0IF;   /* USART0 TX interrupt flag */
-sbit at 0xEA UTX1IF;   /* USART1 TX interrupt flag (shared with I2S TX) */
-sbit at 0xEA I2STXIF;  /* I2S TX interrupt flag (shared with USART1 TX) */
-sbit at 0xEB P1IF;     /* Port1 interrupt flag */
-sbit at 0xEC WDTIF;    /* Watchdog timer interrupt flag */
-
-#define IRCON2_USBIF   (1 << 0)        /* USB interrupt flag (shared with Port2) */
-#define IRCON2_P2IF    (1 << 0)        /* Port2 interrupt flag (shared with USB) */
-#define IRCON2_UTX0IF  (1 << 1)        /* USART0 TX interrupt flag */
-#define IRCON2_UTX1IF  (1 << 2)        /* USART1 TX interrupt flag (shared with I2S TX) */
-#define IRCON2_I2STXIF (1 << 2)        /* I2S TX interrupt flag (shared with USART1 TX) */
-#define IRCON2_P1IF    (1 << 3)        /* Port1 interrupt flag */
-#define IRCON2_WDTIF   (1 << 4)        /* Watchdog timer interrupt flag */
-
-/*
- * IP1 - Interrupt Priority 1
- */
-
-/*
- * Interrupt priority groups:
- *
- * IPG0                RFTXRX          RF              DMA
- * IPG1                ADC             T1              P2INT/USB
- * IPG2                URX0            T2              UTX0
- * IPG3                URX1/I2SRX      T3              UTX1 / I2STX
- * IPG4                ENC             T4              P1INT
- * IPG5                ST              P0INT           WDT
- *
- * Priority = (IP1 << 1) | IP0. Higher priority interrupts served first
- */
-
-sfr at 0xB9 IP1;       /* Interrupt Priority 1 */
-sfr at 0xA9 IP0;       /* Interrupt Priority 0 */
-
-#define IP1_IPG5       (1 << 5)
-#define IP1_IPG4       (1 << 4)
-#define IP1_IPG3       (1 << 3)
-#define IP1_IPG2       (1 << 2)
-#define IP1_IPG1       (1 << 1)
-#define IP1_IPG0       (1 << 0)
-
-#define IP0_IPG5       (1 << 5)
-#define IP0_IPG4       (1 << 4)
-#define IP0_IPG3       (1 << 3)
-#define IP0_IPG2       (1 << 2)
-#define IP0_IPG1       (1 << 1)
-#define IP0_IPG0       (1 << 0)
-
-/*
- * Timer 1
- */
-#define T1CTL_MODE_SUSPENDED   (0 << 0)
-#define T1CTL_MODE_FREE                (1 << 0)
-#define T1CTL_MODE_MODULO      (2 << 0)
-#define T1CTL_MODE_UP_DOWN     (3 << 0)
-#define T1CTL_MODE_MASK                (3 << 0)
-#define T1CTL_DIV_1            (0 << 2)
-#define T1CTL_DIV_8            (1 << 2)
-#define T1CTL_DIV_32           (2 << 2)
-#define T1CTL_DIV_128          (3 << 2)
-#define T1CTL_DIV_MASK         (3 << 2)
-#define T1CTL_OVFIF            (1 << 4)
-#define T1CTL_CH0IF            (1 << 5)
-#define T1CTL_CH1IF            (1 << 6)
-#define T1CTL_CH2IF            (1 << 7)
-
-#define T1CCTL_NO_CAPTURE      (0 << 0)
-#define T1CCTL_CAPTURE_RISING  (1 << 0)
-#define T1CCTL_CAPTURE_FALLING (2 << 0)
-#define T1CCTL_CAPTURE_BOTH    (3 << 0)
-#define T1CCTL_CAPTURE_MASK    (3 << 0)
-
-#define T1CCTL_MODE_CAPTURE    (0 << 2)
-#define T1CCTL_MODE_COMPARE    (1 << 2)
-
-#define T1CTL_CMP_SET          (0 << 3)
-#define T1CTL_CMP_CLEAR                (1 << 3)
-#define T1CTL_CMP_TOGGLE       (2 << 3)
-#define T1CTL_CMP_SET_CLEAR    (3 << 3)
-#define T1CTL_CMP_CLEAR_SET    (4 << 3)
-
-#define T1CTL_IM_DISABLED      (0 << 6)
-#define T1CTL_IM_ENABLED       (1 << 6)
-
-#define T1CTL_CPSEL_NORMAL     (0 << 7)
-#define T1CTL_CPSEL_RF         (1 << 7)
-
-/*
- * Timer 3 and Timer 4
- */
-
-/* Timer count */
-sfr at 0xCA T3CNT;
-sfr at 0xEA T4CNT;
-
-/* Timer control */
-
-sfr at 0xCB T3CTL;
-sfr at 0xEB T4CTL;
-
-#define TxCTL_DIV_1            (0 << 5)
-#define TxCTL_DIV_2            (1 << 5)
-#define TxCTL_DIV_4            (2 << 5)
-#define TxCTL_DIV_8            (3 << 5)
-#define TxCTL_DIV_16           (4 << 5)
-#define TxCTL_DIV_32           (5 << 5)
-#define TxCTL_DIV_64           (6 << 5)
-#define TxCTL_DIV_128          (7 << 5)
-#define TxCTL_START            (1 << 4)
-#define TxCTL_OVFIM            (1 << 3)
-#define TxCTL_CLR              (1 << 2)
-#define TxCTL_MODE_FREE                (0 << 0)
-#define TxCTL_MODE_DOWN                (1 << 0)
-#define TxCTL_MODE_MODULO      (2 << 0)
-#define TxCTL_MODE_UP_DOWN     (3 << 0)
-
-/* Timer 4 channel 0 compare control */
-
-sfr at 0xCC T3CCTL0;
-sfr at 0xCE T3CCTL1;
-sfr at 0xEC T4CCTL0;
-sfr at 0xEE T4CCTL1;
-
-#define TxCCTLy_IM                     (1 << 6)
-#define TxCCTLy_CMP_SET                        (0 << 3)
-#define TxCCTLy_CMP_CLEAR              (1 << 3)
-#define TxCCTLy_CMP_TOGGLE             (2 << 3)
-#define TxCCTLy_CMP_SET_UP_CLEAR_DOWN  (3 << 3)
-#define TxCCTLy_CMP_CLEAR_UP_SET_DOWN  (4 << 3)
-#define TxCCTLy_CMP_SET_CLEAR_FF       (5 << 3)
-#define TxCCTLy_CMP_CLEAR_SET_00       (6 << 3)
-#define TxCCTLy_CMP_MODE_ENABLE                (1 << 2)
-
-/* Timer compare value */
-sfr at 0xCD T3CC0;
-sfr at 0xCF T3CC1;
-sfr at 0xED T4CC0;
-sfr at 0xEF T4CC1;
-
-/*
- * Peripheral control
- */
-
-sfr at 0xf1 PERCFG;
-#define PERCFG_T1CFG_ALT_1      (0 << 6)
-#define PERCFG_T1CFG_ALT_2      (1 << 6)
-#define PERCFG_T1CFG_ALT_MASK   (1 << 6)
-
-#define PERCFG_T3CFG_ALT_1      (0 << 5)
-#define PERCFG_T3CFG_ALT_2      (1 << 5)
-#define PERCFG_T3CFG_ALT_MASK   (1 << 5)
-
-#define PERCFG_T4CFG_ALT_1      (0 << 4)
-#define PERCFG_T4CFG_ALT_2      (1 << 4)
-#define PERCFG_T4CFG_ALT_MASK   (1 << 4)
-
-#define PERCFG_U1CFG_ALT_1      (0 << 1)
-#define PERCFG_U1CFG_ALT_2      (1 << 1)
-#define PERCFG_U1CFG_ALT_MASK   (1 << 1)
-
-#define PERCFG_U0CFG_ALT_1      (0 << 0)
-#define PERCFG_U0CFG_ALT_2      (1 << 0)
-#define PERCFG_U0CFG_ALT_MASK   (1 << 0)
-
-/* directly addressed USB registers */
-__xdata __at (0xde00) volatile uint8_t USBADDR;
-__xdata __at (0xde01) volatile uint8_t USBPOW;
-__xdata __at (0xde02) volatile uint8_t USBIIF;
-
-__xdata __at (0xde04) volatile uint8_t USBOIF;
-
-__xdata __at (0xde06) volatile uint8_t USBCIF;
-
-# define USBCIF_SOFIF          (1 << 3)
-# define USBCIF_RSTIF          (1 << 2)
-# define USBCIF_RESUMEIF       (1 << 1)
-# define USBCIF_SUSPENDIF      (1 << 0)
-
-__xdata __at (0xde07) volatile uint8_t USBIIE;
-
-__xdata __at (0xde09) volatile uint8_t USBOIE;
-
-__xdata __at (0xde0b) volatile uint8_t USBCIE;
-
-# define USBCIE_SOFIE          (1 << 3)
-# define USBCIE_RSTIE          (1 << 2)
-# define USBCIE_RESUMEIE       (1 << 1)
-# define USBCIE_SUSPENDIE      (1 << 0)
-
-__xdata __at (0xde0c) volatile uint8_t USBFRML;
-__xdata __at (0xde0d) volatile uint8_t USBFRMH;
-__xdata __at (0xde0e) volatile uint8_t USBINDEX;
-
-/* indexed USB registers, must set USBINDEX to 0-5 */
-__xdata __at (0xde10) volatile uint8_t USBMAXI;
-__xdata __at (0xde11) volatile uint8_t USBCS0;
-
-# define USBCS0_CLR_SETUP_END          (1 << 7)
-# define USBCS0_CLR_OUTPKT_RDY         (1 << 6)
-# define USBCS0_SEND_STALL             (1 << 5)
-# define USBCS0_SETUP_END              (1 << 4)
-# define USBCS0_DATA_END               (1 << 3)
-# define USBCS0_SENT_STALL             (1 << 2)
-# define USBCS0_INPKT_RDY              (1 << 1)
-# define USBCS0_OUTPKT_RDY             (1 << 0)
-
-__xdata __at (0xde11) volatile uint8_t USBCSIL;
-
-# define USBCSIL_CLR_DATA_TOG          (1 << 6)
-# define USBCSIL_SENT_STALL            (1 << 5)
-# define USBCSIL_SEND_STALL            (1 << 4)
-# define USBCSIL_FLUSH_PACKET          (1 << 3)
-# define USBCSIL_UNDERRUN              (1 << 2)
-# define USBCSIL_PKT_PRESENT           (1 << 1)
-# define USBCSIL_INPKT_RDY             (1 << 0)
-
-__xdata __at (0xde12) volatile uint8_t USBCSIH;
-
-# define USBCSIH_AUTOSET               (1 << 7)
-# define USBCSIH_ISO                   (1 << 6)
-# define USBCSIH_FORCE_DATA_TOG                (1 << 3)
-# define USBCSIH_IN_DBL_BUF            (1 << 0)
-
-__xdata __at (0xde13) volatile uint8_t USBMAXO;
-__xdata __at (0xde14) volatile uint8_t USBCSOL;
-
-# define USBCSOL_CLR_DATA_TOG          (1 << 7)
-# define USBCSOL_SENT_STALL            (1 << 6)
-# define USBCSOL_SEND_STALL            (1 << 5)
-# define USBCSOL_FLUSH_PACKET          (1 << 4)
-# define USBCSOL_DATA_ERROR            (1 << 3)
-# define USBCSOL_OVERRUN               (1 << 2)
-# define USBCSOL_FIFO_FULL             (1 << 1)
-# define USBCSOL_OUTPKT_RDY            (1 << 0)
-
-__xdata __at (0xde15) volatile uint8_t USBCSOH;
-
-# define USBCSOH_AUTOCLEAR             (1 << 7)
-# define USBCSOH_ISO                   (1 << 6)
-# define USBCSOH_OUT_DBL_BUF           (1 << 0)
-
-__xdata __at (0xde16) volatile uint8_t USBCNT0;
-__xdata __at (0xde16) volatile uint8_t USBCNTL;
-__xdata __at (0xde17) volatile uint8_t USBCNTH;
-
-__xdata __at (0xde20) volatile uint8_t USBFIFO[12];
-
-/* ADC Data register, low and high */
-sfr at 0xBA ADCL;
-sfr at 0xBB ADCH;
-__xdata __at (0xDFBA) volatile uint16_t ADCXDATA;
-
-/* ADC Control Register 1 */
-sfr at 0xB4 ADCCON1;
-
-# define ADCCON1_EOC           (1 << 7)        /* conversion complete */
-# define ADCCON1_ST            (1 << 6)        /* start conversion */
-
-# define ADCCON1_STSEL_MASK    (3 << 4)        /* start select */
-# define ADCCON1_STSEL_EXTERNAL        (0 << 4)        /* P2_0 pin triggers */
-# define ADCCON1_STSEL_FULLSPEED (1 << 4)      /* full speed, no waiting */
-# define ADCCON1_STSEL_TIMER1  (2 << 4)        /* timer 1 channel 0 */
-# define ADCCON1_STSEL_START   (3 << 4)        /* set start bit */
-
-# define ADCCON1_RCTRL_MASK    (3 << 2)        /* random number control */
-# define ADCCON1_RCTRL_COMPLETE        (0 << 2)        /* operation completed */
-# define ADCCON1_RCTRL_CLOCK_LFSR (1 << 2)     /* Clock the LFSR once */
-
-/* ADC Control Register 2 */
-sfr at 0xB5 ADCCON2;
-
-# define ADCCON2_SREF_MASK     (3 << 6)        /* reference voltage */
-# define ADCCON2_SREF_1_25V    (0 << 6)        /* internal 1.25V */
-# define ADCCON2_SREF_EXTERNAL (1 << 6)        /* external on AIN7 cc1110 */
-# define ADCCON2_SREF_VDD      (2 << 6)        /* VDD on the AVDD pin */
-# define ADCCON2_SREF_EXTERNAL_DIFF (3 << 6)   /* external on AIN6-7 cc1110 */
-
-# define ADCCON2_SDIV_MASK     (3 << 4)        /* decimation rate */
-# define ADCCON2_SDIV_64       (0 << 4)        /* 7 bits */
-# define ADCCON2_SDIV_128      (1 << 4)        /* 9 bits */
-# define ADCCON2_SDIV_256      (2 << 4)        /* 10 bits */
-# define ADCCON2_SDIV_512      (3 << 4)        /* 12 bits */
-
-# define ADCCON2_SCH_MASK      (0xf << 0)      /* Sequence channel select */
-# define ADCCON2_SCH_SHIFT     0
-# define ADCCON2_SCH_AIN0      (0 << 0)
-# define ADCCON2_SCH_AIN1      (1 << 0)
-# define ADCCON2_SCH_AIN2      (2 << 0)
-# define ADCCON2_SCH_AIN3      (3 << 0)
-# define ADCCON2_SCH_AIN4      (4 << 0)
-# define ADCCON2_SCH_AIN5      (5 << 0)
-# define ADCCON2_SCH_AIN6      (6 << 0)
-# define ADCCON2_SCH_AIN7      (7 << 0)
-# define ADCCON2_SCH_AIN0_AIN1 (8 << 0)
-# define ADCCON2_SCH_AIN2_AIN3 (9 << 0)
-# define ADCCON2_SCH_AIN4_AIN5 (0xa << 0)
-# define ADCCON2_SCH_AIN6_AIN7 (0xb << 0)
-# define ADCCON2_SCH_GND       (0xc << 0)
-# define ADCCON2_SCH_VREF      (0xd << 0)
-# define ADCCON2_SCH_TEMP      (0xe << 0)
-# define ADCCON2_SCH_VDD_3     (0xf << 0)
-
-
-/* ADC Control Register 3 */
-sfr at 0xB6 ADCCON3;
-
-# define ADCCON3_EREF_MASK     (3 << 6)        /* extra conversion reference */
-# define ADCCON3_EREF_1_25     (0 << 6)        /* internal 1.25V */
-# define ADCCON3_EREF_EXTERNAL (1 << 6)        /* external AIN7 cc1110 */
-# define ADCCON3_EREF_VDD      (2 << 6)        /* VDD on the AVDD pin */
-# define ADCCON3_EREF_EXTERNAL_DIFF (3 << 6)   /* external AIN6-7 cc1110 */
-# define ADCCON3_EDIV_MASK     (3 << 4)        /* extral decimation */
-# define ADCCON3_EDIV_64       (0 << 4)        /* 7 bits */
-# define ADCCON3_EDIV_128      (1 << 4)        /* 9 bits */
-# define ADCCON3_EDIV_256      (2 << 4)        /* 10 bits */
-# define ADCCON3_EDIV_512      (3 << 4)        /* 12 bits */
-# define ADCCON3_ECH_MASK      (0xf << 0)      /* Sequence channel select */
-# define ADCCON3_ECH_SHIFT     0
-# define ADCCON3_ECH_AIN0      (0 << 0)
-# define ADCCON3_ECH_AIN1      (1 << 0)
-# define ADCCON3_ECH_AIN2      (2 << 0)
-# define ADCCON3_ECH_AIN3      (3 << 0)
-# define ADCCON3_ECH_AIN4      (4 << 0)
-# define ADCCON3_ECH_AIN5      (5 << 0)
-# define ADCCON3_ECH_AIN6      (6 << 0)
-# define ADCCON3_ECH_AIN7      (7 << 0)
-# define ADCCON3_ECH_AIN0_AIN1 (8 << 0)
-# define ADCCON3_ECH_AIN2_AIN3 (9 << 0)
-# define ADCCON3_ECH_AIN4_AIN5 (0xa << 0)
-# define ADCCON3_ECH_AIN6_AIN7 (0xb << 0)
-# define ADCCON3_ECH_GND       (0xc << 0)
-# define ADCCON3_ECH_VREF      (0xd << 0)
-# define ADCCON3_ECH_TEMP      (0xe << 0)
-# define ADCCON3_ECH_VDD_3     (0xf << 0)
-
-/*
- * ADC configuration register, this selects which
- * GPIO pins are to be used as ADC inputs
- */
-sfr at 0xF2 ADCCFG;
-
-/*
- * Pin selectors, these set which pins are
- * using their peripheral function
- */
-sfr at 0xF3 P0SEL;
-sfr at 0xF4 P1SEL;
-sfr at 0xF5 P2SEL;
-
-#define P2SEL_PRI3P1_USART0            (0 << 6)
-#define P2SEL_PRI3P1_USART1            (1 << 6)
-#define P2SEL_PRI3P1_MASK              (1 << 6)
-#define P2SEL_PRI2P1_USART1            (0 << 5)
-#define P2SEL_PRI2P1_TIMER3            (1 << 5)
-#define P2SEL_PRI1P1_TIMER1            (0 << 4)
-#define P2SEL_PRI1P1_TIMER4            (1 << 4)
-#define P2SEL_PRI0P1_USART0            (0 << 3)
-#define P2SEL_PRI0P1_TIMER1            (1 << 3)
-#define P2SEL_SELP2_4_GPIO             (0 << 2)
-#define P2SEL_SELP2_4_PERIPHERAL       (1 << 2)
-#define P2SEL_SELP2_3_GPIO             (0 << 1)
-#define P2SEL_SELP2_3_PERIPHERAL       (1 << 1)
-#define P2SEL_SELP2_0_GPIO             (0 << 0)
-#define P2SEL_SELP2_0_PERIPHERAL       (1 << 0)
-#define P2SEL_SELP2_0_MASK             (1 << 0)
-
-/*
- * For pins used as GPIOs, these set which are used as outputs
- */
-sfr at 0xFD P0DIR;
-sfr at 0xFE P1DIR;
-sfr at 0xFF P2DIR;
-
-sfr at 0x8F P0INP;
-
-/* Select between tri-state and pull up/down
- * for pins P0_0 - P0_7.
- */
-#define P0INP_MDP0_7_PULL      (0 << 7)
-#define P0INP_MDP0_7_TRISTATE  (1 << 7)
-#define P0INP_MDP0_6_PULL      (0 << 6)
-#define P0INP_MDP0_6_TRISTATE  (1 << 6)
-#define P0INP_MDP0_5_PULL      (0 << 5)
-#define P0INP_MDP0_5_TRISTATE  (1 << 5)
-#define P0INP_MDP0_4_PULL      (0 << 4)
-#define P0INP_MDP0_4_TRISTATE  (1 << 4)
-#define P0INP_MDP0_3_PULL      (0 << 3)
-#define P0INP_MDP0_3_TRISTATE  (1 << 3)
-#define P0INP_MDP0_2_PULL      (0 << 2)
-#define P0INP_MDP0_2_TRISTATE  (1 << 2)
-#define P0INP_MDP0_1_PULL      (0 << 1)
-#define P0INP_MDP0_1_TRISTATE  (1 << 1)
-#define P0INP_MDP0_0_PULL      (0 << 0)
-#define P0INP_MDP0_0_TRISTATE  (1 << 0)
-
-sfr at 0xF6 P1INP;
-
-/* Select between tri-state and pull up/down
- * for pins P1_2 - P1_7. Pins P1_0 and P1_1 are
- * always tri-stated
- */
-#define P1INP_MDP1_7_PULL      (0 << 7)
-#define P1INP_MDP1_7_TRISTATE  (1 << 7)
-#define P1INP_MDP1_6_PULL      (0 << 6)
-#define P1INP_MDP1_6_TRISTATE  (1 << 6)
-#define P1INP_MDP1_5_PULL      (0 << 5)
-#define P1INP_MDP1_5_TRISTATE  (1 << 5)
-#define P1INP_MDP1_4_PULL      (0 << 4)
-#define P1INP_MDP1_4_TRISTATE  (1 << 4)
-#define P1INP_MDP1_3_PULL      (0 << 3)
-#define P1INP_MDP1_3_TRISTATE  (1 << 3)
-#define P1INP_MDP1_2_PULL      (0 << 2)
-#define P1INP_MDP1_2_TRISTATE  (1 << 2)
-
-sfr at 0xF7 P2INP;
-/* P2INP has three extra bits which are used to choose
- * between pull-up and pull-down when they are not tri-stated
- */
-#define P2INP_PDUP2_PULL_UP    (0 << 7)
-#define P2INP_PDUP2_PULL_DOWN  (1 << 7)
-#define P2INP_PDUP1_PULL_UP    (0 << 6)
-#define P2INP_PDUP1_PULL_DOWN  (1 << 6)
-#define P2INP_PDUP0_PULL_UP    (0 << 5)
-#define P2INP_PDUP0_PULL_DOWN  (1 << 5)
-
-/* For the P2 pins, choose between tri-state and pull up/down
- * mode
- */
-#define P2INP_MDP2_4_PULL      (0 << 4)
-#define P2INP_MDP2_4_TRISTATE  (1 << 4)
-#define P2INP_MDP2_3_PULL      (0 << 3)
-#define P2INP_MDP2_3_TRISTATE  (1 << 3)
-#define P2INP_MDP2_2_PULL      (0 << 2)
-#define P2INP_MDP2_2_TRISTATE  (1 << 2)
-#define P2INP_MDP2_1_PULL      (0 << 1)
-#define P2INP_MDP2_1_TRISTATE  (1 << 1)
-#define P2INP_MDP2_0_PULL      (0 << 0)
-#define P2INP_MDP2_0_TRISTATE  (1 << 0)
-
-/* GPIO interrupt status flags */
-sfr at 0x89 P0IFG;
-sfr at 0x8A P1IFG;
-sfr at 0x8B P2IFG;
-
-#define P0IFG_USB_RESUME       (1 << 7)
-
-/* GPIO pins */
-sfr at 0x80 P0;
-
-sbit at 0x80 P0_0;
-sbit at 0x81 P0_1;
-sbit at 0x82 P0_2;
-sbit at 0x83 P0_3;
-sbit at 0x84 P0_4;
-sbit at 0x85 P0_5;
-sbit at 0x86 P0_6;
-sbit at 0x87 P0_7;
-
-sfr at 0x90 P1;
-
-sbit at 0x90 P1_0;
-sbit at 0x91 P1_1;
-sbit at 0x92 P1_2;
-sbit at 0x93 P1_3;
-sbit at 0x94 P1_4;
-sbit at 0x95 P1_5;
-sbit at 0x96 P1_6;
-sbit at 0x97 P1_7;
-
-sfr at 0xa0 P2;
-
-sbit at 0xa0 P2_0;
-sbit at 0xa1 P2_1;
-sbit at 0xa2 P2_2;
-sbit at 0xa3 P2_3;
-sbit at 0xa4 P2_4;
-
-/* DMA controller */
-struct cc_dma_channel {
-       uint8_t src_high;
-       uint8_t src_low;
-       uint8_t dst_high;
-       uint8_t dst_low;
-       uint8_t len_high;
-       uint8_t len_low;
-       uint8_t cfg0;
-       uint8_t cfg1;
-};
-
-# define DMA_LEN_HIGH_VLEN_MASK                (7 << 5)
-# define DMA_LEN_HIGH_VLEN_LEN         (0 << 5)
-# define DMA_LEN_HIGH_VLEN_PLUS_1      (1 << 5)
-# define DMA_LEN_HIGH_VLEN             (2 << 5)
-# define DMA_LEN_HIGH_VLEN_PLUS_2      (3 << 5)
-# define DMA_LEN_HIGH_VLEN_PLUS_3      (4 << 5)
-# define DMA_LEN_HIGH_MASK             (0x1f)
-
-# define DMA_CFG0_WORDSIZE_8           (0 << 7)
-# define DMA_CFG0_WORDSIZE_16          (1 << 7)
-# define DMA_CFG0_TMODE_MASK           (3 << 5)
-# define DMA_CFG0_TMODE_SINGLE         (0 << 5)
-# define DMA_CFG0_TMODE_BLOCK          (1 << 5)
-# define DMA_CFG0_TMODE_REPEATED_SINGLE        (2 << 5)
-# define DMA_CFG0_TMODE_REPEATED_BLOCK (3 << 5)
-
-/*
- * DMA triggers
- */
-# define DMA_CFG0_TRIGGER_NONE         0
-# define DMA_CFG0_TRIGGER_PREV         1
-# define DMA_CFG0_TRIGGER_T1_CH0       2
-# define DMA_CFG0_TRIGGER_T1_CH1       3
-# define DMA_CFG0_TRIGGER_T1_CH2       4
-# define DMA_CFG0_TRIGGER_T2_OVFL      6
-# define DMA_CFG0_TRIGGER_T3_CH0       7
-# define DMA_CFG0_TRIGGER_T3_CH1       8
-# define DMA_CFG0_TRIGGER_T4_CH0       9
-# define DMA_CFG0_TRIGGER_T4_CH1       10
-# define DMA_CFG0_TRIGGER_IOC_0                12
-# define DMA_CFG0_TRIGGER_IOC_1                13
-# define DMA_CFG0_TRIGGER_URX0         14
-# define DMA_CFG0_TRIGGER_UTX0         15
-# define DMA_CFG0_TRIGGER_URX1         16
-# define DMA_CFG0_TRIGGER_UTX1         17
-# define DMA_CFG0_TRIGGER_FLASH                18
-# define DMA_CFG0_TRIGGER_RADIO                19
-# define DMA_CFG0_TRIGGER_ADC_CHALL    20
-# define DMA_CFG0_TRIGGER_ADC_CH0      21
-# define DMA_CFG0_TRIGGER_ADC_CH1      22
-# define DMA_CFG0_TRIGGER_ADC_CH2      23
-# define DMA_CFG0_TRIGGER_ADC_CH3      24
-# define DMA_CFG0_TRIGGER_ADC_CH4      25
-# define DMA_CFG0_TRIGGER_ADC_CH5      26
-# define DMA_CFG0_TRIGGER_ADC_CH6      27
-# define DMA_CFG0_TRIGGER_I2SRX                27
-# define DMA_CFG0_TRIGGER_ADC_CH7      28
-# define DMA_CFG0_TRIGGER_I2STX                28
-# define DMA_CFG0_TRIGGER_ENC_DW       29
-# define DMA_CFG0_TRIGGER_DNC_UP       30
-
-# define DMA_CFG1_SRCINC_MASK          (3 << 6)
-# define DMA_CFG1_SRCINC_0             (0 << 6)
-# define DMA_CFG1_SRCINC_1             (1 << 6)
-# define DMA_CFG1_SRCINC_2             (2 << 6)
-# define DMA_CFG1_SRCINC_MINUS_1       (3 << 6)
-
-# define DMA_CFG1_DESTINC_MASK         (3 << 4)
-# define DMA_CFG1_DESTINC_0            (0 << 4)
-# define DMA_CFG1_DESTINC_1            (1 << 4)
-# define DMA_CFG1_DESTINC_2            (2 << 4)
-# define DMA_CFG1_DESTINC_MINUS_1      (3 << 4)
-
-# define DMA_CFG1_IRQMASK              (1 << 3)
-# define DMA_CFG1_M8                   (1 << 2)
-
-# define DMA_CFG1_PRIORITY_MASK                (3 << 0)
-# define DMA_CFG1_PRIORITY_LOW         (0 << 0)
-# define DMA_CFG1_PRIORITY_NORMAL      (1 << 0)
-# define DMA_CFG1_PRIORITY_HIGH                (2 << 0)
-
-/*
- * DMAARM - DMA Channel Arm
- */
-
-sfr at 0xD6 DMAARM;
-
-# define DMAARM_ABORT                  (1 << 7)
-# define DMAARM_DMAARM4                        (1 << 4)
-# define DMAARM_DMAARM3                        (1 << 3)
-# define DMAARM_DMAARM2                        (1 << 2)
-# define DMAARM_DMAARM1                        (1 << 1)
-# define DMAARM_DMAARM0                        (1 << 0)
-
-/*
- * DMAREQ - DMA Channel Start Request and Status
- */
-
-sfr at 0xD7 DMAREQ;
-
-# define DMAREQ_DMAREQ4                        (1 << 4)
-# define DMAREQ_DMAREQ3                        (1 << 3)
-# define DMAREQ_DMAREQ2                        (1 << 2)
-# define DMAREQ_DMAREQ1                        (1 << 1)
-# define DMAREQ_DMAREQ0                        (1 << 0)
-
-/*
- * DMA configuration 0 address
- */
-
-sfr at 0xD5 DMA0CFGH;
-sfr at 0xD4 DMA0CFGL;
-
-/*
- * DMA configuration 1-4 address
- */
-
-sfr at 0xD3 DMA1CFGH;
-sfr at 0xD2 DMA1CFGL;
-
-/*
- * DMAIRQ - DMA Interrupt Flag
- */
-
-sfr at 0xD1 DMAIRQ;
-
-# define DMAIRQ_DMAIF4                 (1 << 4)
-# define DMAIRQ_DMAIF3                 (1 << 3)
-# define DMAIRQ_DMAIF2                 (1 << 2)
-# define DMAIRQ_DMAIF1                 (1 << 1)
-# define DMAIRQ_DMAIF0                 (1 << 0)
-
-/*
- * UART registers
- */
-
-/* USART config/status registers */
-sfr at 0x86 U0CSR;
-sfr at 0xF8 U1CSR;
-
-# define UxCSR_MODE_UART               (1 << 7)
-# define UxCSR_MODE_SPI                        (0 << 7)
-# define UxCSR_RE                      (1 << 6)
-# define UxCSR_SLAVE                   (1 << 5)
-# define UxCSR_MASTER                  (0 << 5)
-# define UxCSR_FE                      (1 << 4)
-# define UxCSR_ERR                     (1 << 3)
-# define UxCSR_RX_BYTE                 (1 << 2)
-# define UxCSR_TX_BYTE                 (1 << 1)
-# define UxCSR_ACTIVE                  (1 << 0)
-
-/* UART configuration registers */
-sfr at 0xc4 U0UCR;
-sfr at 0xfb U1UCR;
-
-# define UxUCR_FLUSH                    (1 << 7)
-# define UxUCR_FLOW_DISABLE             (0 << 6)
-# define UxUCR_FLOW_ENABLE              (1 << 6)
-# define UxUCR_D9_EVEN_PARITY           (0 << 5)
-# define UxUCR_D9_ODD_PARITY            (1 << 5)
-# define UxUCR_BIT9_8_BITS              (0 << 4)
-# define UxUCR_BIT9_9_BITS              (1 << 4)
-# define UxUCR_PARITY_DISABLE           (0 << 3)
-# define UxUCR_PARITY_ENABLE            (1 << 3)
-# define UxUCR_SPB_1_STOP_BIT           (0 << 2)
-# define UxUCR_SPB_2_STOP_BITS          (1 << 2)
-# define UxUCR_STOP_LOW                 (0 << 1)
-# define UxUCR_STOP_HIGH                (1 << 1)
-# define UxUCR_START_LOW                (0 << 0)
-# define UxUCR_START_HIGH               (1 << 0)
-
-/* USART General configuration registers (mostly SPI) */
-sfr at 0xc5 U0GCR;
-sfr at 0xfc U1GCR;
-
-# define UxGCR_CPOL_NEGATIVE           (0 << 7)
-# define UxGCR_CPOL_POSITIVE           (1 << 7)
-# define UxGCR_CPHA_FIRST_EDGE         (0 << 6)
-# define UxGCR_CPHA_SECOND_EDGE                (1 << 6)
-# define UxGCR_ORDER_LSB               (0 << 5)
-# define UxGCR_ORDER_MSB               (1 << 5)
-# define UxGCR_BAUD_E_MASK             (0x1f)
-# define UxGCR_BAUD_E_SHIFT            0
-
-/* USART data registers */
-sfr at 0xc1 U0DBUF;
-__xdata __at (0xDFC1) volatile uint8_t U0DBUFXADDR;
-sfr at 0xf9 U1DBUF;
-__xdata __at (0xDFF9) volatile uint8_t U1DBUFXADDR;
-
-/* USART baud rate registers, M value */
-sfr at 0xc2 U0BAUD;
-sfr at 0xfa U1BAUD;
-
-/* Radio */
-
-sfr at 0xD9 RFD;
-__xdata at (0xDFD9) volatile uint8_t RFDXADDR;
-
-sfr at 0xE9 RFIF;
-#define RFIF_IM_TXUNF  (1 << 7)
-#define RFIF_IM_RXOVF  (1 << 6)
-#define RFIF_IM_TIMEOUT        (1 << 5)
-#define RFIF_IM_DONE   (1 << 4)
-#define RFIF_IM_CS     (1 << 3)
-#define RFIF_IM_PQT    (1 << 2)
-#define RFIF_IM_CCA    (1 << 1)
-#define RFIF_IM_SFD    (1 << 0)
-
-sfr at 0xE1 RFST;
-
-#define RFST_SFSTXON   0x00
-#define RFST_SCAL      0x01
-#define RFST_SRX       0x02
-#define RFST_STX       0x03
-#define RFST_SIDLE     0x04
-
-__xdata __at (0xdf00) uint8_t RF[0x3c];
-
-__xdata __at (0xdf2f) uint8_t RF_IOCFG2;
-#define RF_IOCFG2_OFF  0x2f
-
-__xdata __at (0xdf30) uint8_t RF_IOCFG1;
-#define RF_IOCFG1_OFF  0x30
-
-__xdata __at (0xdf31) uint8_t RF_IOCFG0;
-#define RF_IOCFG0_OFF  0x31
-
-__xdata __at (0xdf00) uint8_t RF_SYNC1;
-#define RF_SYNC1_OFF   0x00
-
-__xdata __at (0xdf01) uint8_t RF_SYNC0;
-#define RF_SYNC0_OFF   0x01
-
-__xdata __at (0xdf02) uint8_t RF_PKTLEN;
-#define RF_PKTLEN_OFF  0x02
-
-__xdata __at (0xdf03) uint8_t RF_PKTCTRL1;
-#define RF_PKTCTRL1_OFF        0x03
-#define PKTCTRL1_PQT_MASK                      (0x7 << 5)
-#define PKTCTRL1_PQT_SHIFT                     5
-#define PKTCTRL1_APPEND_STATUS                 (1 << 2)
-#define PKTCTRL1_ADR_CHK_NONE                  (0 << 0)
-#define PKTCTRL1_ADR_CHK_NO_BROADCAST          (1 << 0)
-#define PKTCTRL1_ADR_CHK_00_BROADCAST          (2 << 0)
-#define PKTCTRL1_ADR_CHK_00_FF_BROADCAST       (3 << 0)
-
-/* If APPEND_STATUS is used, two bytes will be added to the packet data */
-#define PKT_APPEND_STATUS_0_RSSI_MASK          (0xff)
-#define PKT_APPEND_STATUS_0_RSSI_SHIFT         0
-#define PKT_APPEND_STATUS_1_CRC_OK             (1 << 7)
-#define PKT_APPEND_STATUS_1_LQI_MASK           (0x7f)
-#define PKT_APPEND_STATUS_1_LQI_SHIFT          0
-
-__xdata __at (0xdf04) uint8_t RF_PKTCTRL0;
-#define RF_PKTCTRL0_OFF        0x04
-#define RF_PKTCTRL0_WHITE_DATA                 (1 << 6)
-#define RF_PKTCTRL0_PKT_FORMAT_NORMAL          (0 << 4)
-#define RF_PKTCTRL0_PKT_FORMAT_RANDOM          (2 << 4)
-#define RF_PKTCTRL0_CRC_EN                     (1 << 2)
-#define RF_PKTCTRL0_LENGTH_CONFIG_FIXED                (0 << 0)
-#define RF_PKTCTRL0_LENGTH_CONFIG_VARIABLE     (1 << 0)
-
-__xdata __at (0xdf05) uint8_t RF_ADDR;
-#define RF_ADDR_OFF    0x05
-
-__xdata __at (0xdf06) uint8_t RF_CHANNR;
-#define RF_CHANNR_OFF  0x06
-
-__xdata __at (0xdf07) uint8_t RF_FSCTRL1;
-#define RF_FSCTRL1_OFF 0x07
-
-#define RF_FSCTRL1_FREQ_IF_SHIFT       (0)
-
-__xdata __at (0xdf08) uint8_t RF_FSCTRL0;
-#define RF_FSCTRL0_OFF 0x08
-
-#define RF_FSCTRL0_FREQOFF_SHIFT       (0)
-
-__xdata __at (0xdf09) uint8_t RF_FREQ2;
-#define RF_FREQ2_OFF   0x09
-
-__xdata __at (0xdf0a) uint8_t RF_FREQ1;
-#define RF_FREQ1_OFF   0x0a
-
-__xdata __at (0xdf0b) uint8_t RF_FREQ0;
-#define RF_FREQ0_OFF   0x0b
-
-__xdata __at (0xdf0c) uint8_t RF_MDMCFG4;
-#define RF_MDMCFG4_OFF 0x0c
-
-#define RF_MDMCFG4_CHANBW_E_SHIFT      6
-#define RF_MDMCFG4_CHANBW_M_SHIFT      4
-#define RF_MDMCFG4_DRATE_E_SHIFT       0
-
-__xdata __at (0xdf0d) uint8_t RF_MDMCFG3;
-#define RF_MDMCFG3_OFF 0x0d
-
-#define RF_MDMCFG3_DRATE_M_SHIFT       0
-
-__xdata __at (0xdf0e) uint8_t RF_MDMCFG2;
-#define RF_MDMCFG2_OFF 0x0e
-
-#define RF_MDMCFG2_DEM_DCFILT_OFF      (1 << 7)
-#define RF_MDMCFG2_DEM_DCFILT_ON       (0 << 7)
-
-#define RF_MDMCFG2_MOD_FORMAT_MASK     (7 << 4)
-#define RF_MDMCFG2_MOD_FORMAT_2_FSK    (0 << 4)
-#define RF_MDMCFG2_MOD_FORMAT_GFSK     (1 << 4)
-#define RF_MDMCFG2_MOD_FORMAT_ASK_OOK  (3 << 4)
-#define RF_MDMCFG2_MOD_FORMAT_MSK      (7 << 4)
-
-#define RF_MDMCFG2_MANCHESTER_EN       (1 << 3)
-
-#define RF_MDMCFG2_SYNC_MODE_MASK              (0x7 << 0)
-#define RF_MDMCFG2_SYNC_MODE_NONE              (0x0 << 0)
-#define RF_MDMCFG2_SYNC_MODE_15_16             (0x1 << 0)
-#define RF_MDMCFG2_SYNC_MODE_16_16             (0x2 << 0)
-#define RF_MDMCFG2_SYNC_MODE_30_32             (0x3 << 0)
-#define RF_MDMCFG2_SYNC_MODE_NONE_THRES                (0x4 << 0)
-#define RF_MDMCFG2_SYNC_MODE_15_16_THRES       (0x5 << 0)
-#define RF_MDMCFG2_SYNC_MODE_16_16_THRES       (0x6 << 0)
-#define RF_MDMCFG2_SYNC_MODE_30_32_THRES       (0x7 << 0)
-
-__xdata __at (0xdf0f) uint8_t RF_MDMCFG1;
-#define RF_MDMCFG1_OFF 0x0f
-
-#define RF_MDMCFG1_FEC_EN                      (1 << 7)
-#define RF_MDMCFG1_FEC_DIS                     (0 << 7)
-
-#define RF_MDMCFG1_NUM_PREAMBLE_MASK           (7 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_2              (0 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_3              (1 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_4              (2 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_6              (3 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_8              (4 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_12             (5 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_16             (6 << 4)
-#define RF_MDMCFG1_NUM_PREAMBLE_24             (7 << 4)
-
-#define RF_MDMCFG1_CHANSPC_E_MASK              (3 << 0)
-#define RF_MDMCFG1_CHANSPC_E_SHIFT             (0)
-
-__xdata __at (0xdf10) uint8_t RF_MDMCFG0;
-#define RF_MDMCFG0_OFF 0x10
-
-#define RF_MDMCFG0_CHANSPC_M_SHIFT             (0)
-
-__xdata __at (0xdf11) uint8_t RF_DEVIATN;
-#define RF_DEVIATN_OFF 0x11
-
-#define RF_DEVIATN_DEVIATION_E_SHIFT           4
-#define RF_DEVIATN_DEVIATION_M_SHIFT           0
-
-__xdata __at (0xdf12) uint8_t RF_MCSM2;
-#define RF_MCSM2_OFF   0x12
-#define RF_MCSM2_RX_TIME_RSSI                  (1 << 4)
-#define RF_MCSM2_RX_TIME_QUAL                  (1 << 3)
-#define RF_MCSM2_RX_TIME_MASK                  (0x7)
-#define RF_MCSM2_RX_TIME_SHIFT                 0
-#define RF_MCSM2_RX_TIME_END_OF_PACKET         (7)
-
-__xdata __at (0xdf13) uint8_t RF_MCSM1;
-#define RF_MCSM1_OFF   0x13
-#define RF_MCSM1_CCA_MODE_ALWAYS                       (0 << 4)
-#define RF_MCSM1_CCA_MODE_RSSI_BELOW                   (1 << 4)
-#define RF_MCSM1_CCA_MODE_UNLESS_RECEIVING             (2 << 4)
-#define RF_MCSM1_CCA_MODE_RSSI_BELOW_UNLESS_RECEIVING  (3 << 4)
-#define RF_MCSM1_RXOFF_MODE_IDLE                       (0 << 2)
-#define RF_MCSM1_RXOFF_MODE_FSTXON                     (1 << 2)
-#define RF_MCSM1_RXOFF_MODE_TX                         (2 << 2)
-#define RF_MCSM1_RXOFF_MODE_RX                         (3 << 2)
-#define RF_MCSM1_TXOFF_MODE_IDLE                       (0 << 0)
-#define RF_MCSM1_TXOFF_MODE_FSTXON                     (1 << 0)
-#define RF_MCSM1_TXOFF_MODE_TX                         (2 << 0)
-#define RF_MCSM1_TXOFF_MODE_RX                         (3 << 0)
-
-__xdata __at (0xdf14) uint8_t RF_MCSM0;
-#define RF_MCSM0_OFF   0x14
-#define RF_MCSM0_FS_AUTOCAL_NEVER              (0 << 4)
-#define RF_MCSM0_FS_AUTOCAL_FROM_IDLE          (1 << 4)
-#define RF_MCSM0_FS_AUTOCAL_TO_IDLE            (2 << 4)
-#define RF_MCSM0_FS_AUTOCAL_TO_IDLE_EVERY_4    (3 << 4)
-#define RF_MCSM0_MAGIC_3                       (1 << 3)
-#define RF_MCSM0_MAGIC_2                       (1 << 2)
-#define RF_MCSM0_CLOSE_IN_RX_0DB               (0 << 0)
-#define RF_MCSM0_CLOSE_IN_RX_6DB               (1 << 0)
-#define RF_MCSM0_CLOSE_IN_RX_12DB              (2 << 0)
-#define RF_MCSM0_CLOSE_IN_RX_18DB              (3 << 0)
-
-__xdata __at (0xdf15) uint8_t RF_FOCCFG;
-#define RF_FOCCFG_OFF  0x15
-#define RF_FOCCFG_FOC_BS_CS_GATE               (1 << 5)
-#define RF_FOCCFG_FOC_PRE_K_1K                 (0 << 3)
-#define RF_FOCCFG_FOC_PRE_K_2K                 (1 << 3)
-#define RF_FOCCFG_FOC_PRE_K_3K                 (2 << 3)
-#define RF_FOCCFG_FOC_PRE_K_4K                 (3 << 3)
-#define RF_FOCCFG_FOC_POST_K_PRE_K             (0 << 2)
-#define RF_FOCCFG_FOC_POST_K_PRE_K_OVER_2      (1 << 2)
-#define RF_FOCCFG_FOC_LIMIT_0                  (0 << 0)
-#define RF_FOCCFG_FOC_LIMIT_BW_OVER_8          (1 << 0)
-#define RF_FOCCFG_FOC_LIMIT_BW_OVER_4          (2 << 0)
-#define RF_FOCCFG_FOC_LIMIT_BW_OVER_2          (3 << 0)
-
-__xdata __at (0xdf16) uint8_t RF_BSCFG;
-#define RF_BSCFG_OFF   0x16
-#define RF_BSCFG_BS_PRE_K_1K                   (0 << 6)
-#define RF_BSCFG_BS_PRE_K_2K                   (1 << 6)
-#define RF_BSCFG_BS_PRE_K_3K                   (2 << 6)
-#define RF_BSCFG_BS_PRE_K_4K                   (3 << 6)
-#define RF_BSCFG_BS_PRE_KP_1KP                 (0 << 4)
-#define RF_BSCFG_BS_PRE_KP_2KP                 (1 << 4)
-#define RF_BSCFG_BS_PRE_KP_3KP                 (2 << 4)
-#define RF_BSCFG_BS_PRE_KP_4KP                 (3 << 4)
-#define RF_BSCFG_BS_POST_KI_PRE_KI             (0 << 3)
-#define RF_BSCFG_BS_POST_KI_PRE_KI_OVER_2      (1 << 3)
-#define RF_BSCFG_BS_POST_KP_PRE_KP             (0 << 2)
-#define RF_BSCFG_BS_POST_KP_PRE_KP_OVER_2      (1 << 2)
-#define RF_BSCFG_BS_LIMIT_0                    (0 << 0)
-#define RF_BSCFG_BS_LIMIT_3_125                        (1 << 0)
-#define RF_BSCFG_BS_LIMIT_6_25                 (2 << 0)
-#define RF_BSCFG_BS_LIMIT_12_5                 (3 << 0)
-
-__xdata __at (0xdf17) uint8_t RF_AGCCTRL2;
-#define RF_AGCCTRL2_OFF        0x17
-
-__xdata __at (0xdf18) uint8_t RF_AGCCTRL1;
-#define RF_AGCCTRL1_OFF        0x18
-
-__xdata __at (0xdf19) uint8_t RF_AGCCTRL0;
-#define RF_AGCCTRL0_OFF        0x19
-
-__xdata __at (0xdf1a) uint8_t RF_FREND1;
-#define RF_FREND1_OFF  0x1a
-
-#define RF_FREND1_LNA_CURRENT_SHIFT            6
-#define RF_FREND1_LNA2MIX_CURRENT_SHIFT                4
-#define RF_FREND1_LODIV_BUF_CURRENT_RX_SHIFT   2
-#define RF_FREND1_MIX_CURRENT_SHIFT            0
-
-__xdata __at (0xdf1b) uint8_t RF_FREND0;
-#define RF_FREND0_OFF  0x1b
-
-#define RF_FREND0_LODIV_BUF_CURRENT_TX_MASK    (0x3 << 4)
-#define RF_FREND0_LODIV_BUF_CURRENT_TX_SHIFT   4
-#define RF_FREND0_PA_POWER_MASK                        (0x7)
-#define RF_FREND0_PA_POWER_SHIFT               0
-
-__xdata __at (0xdf1c) uint8_t RF_FSCAL3;
-#define RF_FSCAL3_OFF  0x1c
-
-__xdata __at (0xdf1d) uint8_t RF_FSCAL2;
-#define RF_FSCAL2_OFF  0x1d
-
-__xdata __at (0xdf1e) uint8_t RF_FSCAL1;
-#define RF_FSCAL1_OFF  0x1e
-
-__xdata __at (0xdf1f) uint8_t RF_FSCAL0;
-#define RF_FSCAL0_OFF  0x1f
-
-__xdata __at (0xdf23) uint8_t RF_TEST2;
-#define RF_TEST2_OFF   0x23
-
-#define RF_TEST2_NORMAL_MAGIC          0x88
-#define RF_TEST2_RX_LOW_DATA_RATE_MAGIC        0x81
-
-__xdata __at (0xdf24) uint8_t RF_TEST1;
-#define RF_TEST1_OFF   0x24
-
-#define RF_TEST1_TX_MAGIC              0x31
-#define RF_TEST1_RX_LOW_DATA_RATE_MAGIC        0x35
-
-__xdata __at (0xdf25) uint8_t RF_TEST0;
-#define RF_TEST0_OFF   0x25
-
-#define RF_TEST0_7_2_MASK              (0xfc)
-#define RF_TEST0_VCO_SEL_CAL_EN                (1 << 1)
-#define RF_TEST0_0_MASK                        (1)
-
-/* These are undocumented, and must be computed
- * using the provided tool.
- */
-__xdata __at (0xdf27) uint8_t RF_PA_TABLE7;
-#define RF_PA_TABLE7_OFF       0x27
-
-__xdata __at (0xdf28) uint8_t RF_PA_TABLE6;
-#define RF_PA_TABLE6_OFF       0x28
-
-__xdata __at (0xdf29) uint8_t RF_PA_TABLE5;
-#define RF_PA_TABLE5_OFF       0x29
-
-__xdata __at (0xdf2a) uint8_t RF_PA_TABLE4;
-#define RF_PA_TABLE4_OFF       0x2a
-
-__xdata __at (0xdf2b) uint8_t RF_PA_TABLE3;
-#define RF_PA_TABLE3_OFF       0x2b
-
-__xdata __at (0xdf2c) uint8_t RF_PA_TABLE2;
-#define RF_PA_TABLE2_OFF       0x2c
-
-__xdata __at (0xdf2d) uint8_t RF_PA_TABLE1;
-#define RF_PA_TABLE1_OFF       0x2d
-
-__xdata __at (0xdf2e) uint8_t RF_PA_TABLE0;
-#define RF_PA_TABLE0_OFF       0x2e
-
-__xdata __at (0xdf36) uint8_t RF_PARTNUM;
-#define RF_PARTNUM_OFF 0x36
-
-__xdata __at (0xdf37) uint8_t RF_VERSION;
-#define RF_VERSION_OFF 0x37
-
-__xdata __at (0xdf38) uint8_t RF_FREQEST;
-#define RF_FREQEST_OFF 0x38
-
-__xdata __at (0xdf39) uint8_t RF_LQI;
-#define RF_LQI_OFF     0x39
-
-#define RF_LQI_CRC_OK                  (1 << 7)
-#define RF_LQI_LQI_EST_MASK            (0x7f)
-
-__xdata __at (0xdf3a) uint8_t RF_RSSI;
-#define RF_RSSI_OFF    0x3a
-
-__xdata __at (0xdf3b) uint8_t RF_MARCSTATE;
-#define RF_MARCSTATE_OFF       0x3b
-
-#define RF_MARCSTATE_MASK              0x1f
-#define RF_MARCSTATE_SLEEP             0x00
-#define RF_MARCSTATE_IDLE              0x01
-#define RF_MARCSTATE_VCOON_MC          0x03
-#define RF_MARCSTATE_REGON_MC          0x04
-#define RF_MARCSTATE_MANCAL            0x05
-#define RF_MARCSTATE_VCOON             0x06
-#define RF_MARCSTATE_REGON             0x07
-#define RF_MARCSTATE_STARTCAL          0x08
-#define RF_MARCSTATE_BWBOOST           0x09
-#define RF_MARCSTATE_FS_LOCK           0x0a
-#define RF_MARCSTATE_IFADCON           0x0b
-#define RF_MARCSTATE_ENDCAL            0x0c
-#define RF_MARCSTATE_RX                        0x0d
-#define RF_MARCSTATE_RX_END            0x0e
-#define RF_MARCSTATE_RX_RST            0x0f
-#define RF_MARCSTATE_TXRX_SWITCH       0x10
-#define RF_MARCSTATE_RX_OVERFLOW       0x11
-#define RF_MARCSTATE_FSTXON            0x12
-#define RF_MARCSTATE_TX                        0x13
-#define RF_MARCSTATE_TX_END            0x14
-#define RF_MARCSTATE_RXTX_SWITCH       0x15
-#define RF_MARCSTATE_TX_UNDERFLOW      0x16
-
-
-__xdata __at (0xdf3c) uint8_t RF_PKTSTATUS;
-#define RF_PKTSTATUS_OFF       0x3c
-
-#define RF_PKTSTATUS_CRC_OK            (1 << 7)
-#define RF_PKTSTATUS_CS                        (1 << 6)
-#define RF_PKTSTATUS_PQT_REACHED       (1 << 5)
-#define RF_PKTSTATUS_CCA               (1 << 4)
-#define RF_PKTSTATUS_SFD               (1 << 3)
-
-__xdata __at (0xdf3d) uint8_t RF_VCO_VC_DAC;
-#define RF_VCO_VC_DAC_OFF      0x3d
-
-#endif
diff --git a/src/cc1111/Makefile.cc1111 b/src/cc1111/Makefile.cc1111
new file mode 100644 (file)
index 0000000..0e19603
--- /dev/null
@@ -0,0 +1,38 @@
+CC=sdcc
+
+CFLAGS=--model-small --debug --opt-code-speed -DCODESIZE=$(CODESIZE)
+
+CFLAGS += $(PRODUCT_DEF) -I. -I.. -I../core -I../cc1111 -I../drivers -I../product
+
+CODESIZE ?= 0x8000
+
+LDFLAGS=--out-fmt-ihx --code-loc 0x0000 --code-size $(CODESIZE) \
+       --xram-loc 0xf000 --xram-size 0xda2 --iram-size 0xff
+
+REL=$(SRC:.c=.rel) ao_product.rel
+ADB=$(REL:.rel=.adb)
+ASM=$(REL:.rel=.asm)
+LNK=$(REL:.rel=.lnk)
+LST=$(REL:.rel=.lst)
+RST=$(REL:.rel=.rst)
+SYM=$(REL:.rel=.sym)
+
+PCDB=$(PROG:.ihx=.cdb)
+PLNK=$(PROG:.ihx=.lnk)
+PMAP=$(PROG:.ihx=.map)
+PMEM=$(PROG:.ihx=.mem)
+PAOM=$(PROG:.ihx=)
+
+%.rel : %.c $(INC)
+       $(call quiet,CC,$(PRODUCT_DEF)) $(CFLAGS) -c -o$@ $<
+
+all:
+
+clean-cc1111:
+       rm -f *.adb *.asm *.lnk *.lst *.rel *.rst *.sym
+       rm -f $(PROGNAME)-*
+       rm -f ao_product.h
+       rm -f ../$(PROGNAME)-*
+
+../ao_kalman.h:
+       +(cd .. && make ao_kalman.h)
diff --git a/src/cc1111/_bp.c b/src/cc1111/_bp.c
new file mode 100644 (file)
index 0000000..6bf135b
--- /dev/null
@@ -0,0 +1,26 @@
+/*-------------------------------------------------------------------------
+
+  _bp.c :- just declares bp as a variable
+
+             Written By -  Sandeep Dutta . sandeep.dutta@usa.net (1999)
+
+   This library is free software; you can redistribute it and/or modify it
+   under the terms of the GNU Library General Public License as published by the
+   Free Software Foundation; either version 2, or (at your option) any
+   later version.
+
+   This library 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 Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   In other words, you are welcome to use, share and improve this program.
+   You are forbidden to forbid anyone else to use, share and improve
+   what you give them.   Help stamp out software-hoarding!
+-------------------------------------------------------------------------*/
+
+__data unsigned char bp ;
diff --git a/src/cc1111/ao_adc.c b/src/cc1111/ao_adc.c
new file mode 100644 (file)
index 0000000..f7b5228
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+volatile __xdata struct ao_data        ao_data_ring[AO_DATA_RING];
+volatile __data uint8_t                ao_data_head;
+
+void
+ao_adc_poll(void)
+{
+#if HAS_ACCEL_REF
+       ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 2;
+#else
+# ifdef TELENANO_V_0_1
+       ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 1;
+# else
+       ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 0;
+# endif
+#endif
+}
+
+void
+ao_data_get(__xdata struct ao_data *packet)
+{
+#if HAS_FLIGHT
+       uint8_t i = ao_data_ring_prev(ao_sample_data);
+#else
+       uint8_t i = ao_data_ring_prev(ao_data_head);
+#endif
+       ao_xmemcpy(packet, (void __xdata *) &ao_data_ring[i], sizeof (struct ao_data));
+}
+
+void
+ao_adc_isr(void) __interrupt 1
+{
+       uint8_t sequence;
+       uint8_t __xdata *a;
+
+       sequence = (ADCCON2 & ADCCON2_SCH_MASK) >> ADCCON2_SCH_SHIFT;
+#if TELEMETRUM_V_0_1 || TELEMETRUM_V_0_2 || TELEMETRUM_V_1_0 || TELEMETRUM_V_1_1 || TELEMETRUM_V_1_2 || TELELAUNCH_V_0_1
+       /* TeleMetrum readings */
+#if HAS_ACCEL_REF
+       if (sequence == 2) {
+               a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.accel_ref);
+               sequence = 0;
+       } else
+#endif
+       {
+               if (sequence == ADCCON3_ECH_TEMP)
+                       sequence = 2;
+               a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.accel + sequence);
+               sequence++;
+       }
+#define GOT_ADC
+       a[0] = ADCL;
+       a[1] = ADCH;
+       if (sequence < 6) {
+#if HAS_EXTERNAL_TEMP == 0
+               /* start next channel conversion */
+               /* v0.2 replaces external temp sensor with internal one */
+               if (sequence == 2)
+                       ADCCON3 = ADCCON3_EREF_1_25 | ADCCON3_EDIV_512 | ADCCON3_ECH_TEMP;
+               else
+#endif
+                       ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | sequence;
+       }
+#endif
+
+#if TELEMINI_V_1_0 || TELENANO_V_0_1
+       /* TeleMini readings */
+       a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.pres);
+#if TELEMINI_V_1_0
+       switch (sequence) {
+       case 0:
+               /* pressure */
+               a += 0;
+               sequence = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 1;
+               break;
+       case 1:
+               /* drogue sense */
+               a += 6;
+               sequence = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 2;
+               break;
+       case 2:
+               /* main sense */
+               a += 8;
+               sequence = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 3;
+               break;
+       case 3:
+               /* battery */
+               a += 4;
+               sequence = ADCCON3_EREF_1_25 | ADCCON3_EDIV_512 | ADCCON3_ECH_TEMP;
+               break;
+       case ADCCON3_ECH_TEMP:
+               a += 2;
+               sequence = 0;
+               break;
+       }
+#define GOT_ADC
+#endif
+#ifdef TELENANO_V_0_1
+       switch (sequence) {
+       case 1:
+               /* pressure */
+               a += 0;
+               sequence = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | 3;
+               break;
+       case 3:
+               /* battery */
+               a += 4;
+               sequence = ADCCON3_EREF_1_25 | ADCCON3_EDIV_512 | ADCCON3_ECH_TEMP;
+               break;
+       case ADCCON3_ECH_TEMP:
+               a += 2;
+               sequence = 0;
+               break;
+       }
+#define GOT_ADC
+#endif
+       a[0] = ADCL;
+       a[1] = ADCH;
+       if (sequence) {
+               /* Start next conversion */
+               ADCCON3 = sequence;
+       }
+#endif /* telemini || telenano */
+
+#ifdef TELEFIRE_V_0_1
+       a = (uint8_t __xdata *) (&ao_data_ring[ao_data_head].adc.sense[0] + sequence);
+       a[0] = ADCL;
+       a[1] = ADCH;
+       if (sequence < 5)
+               ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | (sequence + 1);
+#define GOT_ADC
+#endif /* TELEFIRE_V_0_1 */
+
+#ifndef GOT_ADC
+#error No known ADC configuration set
+#endif
+
+       else {
+               /* record this conversion series */
+               ao_data_ring[ao_data_head].tick = ao_time();
+               ao_data_head = ao_data_ring_next(ao_data_head);
+               ao_wakeup(DATA_TO_XDATA(&ao_data_head));
+       }
+}
+
+static void
+ao_adc_dump(void) __reentrant
+{
+       static __xdata struct ao_data   packet;
+       ao_data_get(&packet);
+#ifndef AO_ADC_DUMP
+       printf("tick: %5u accel: %5d pres: %5d temp: %5d batt: %5d drogue: %5d main: %5d\n",
+              packet.tick, packet.adc.accel, packet.adc.pres, packet.adc.temp,
+              packet.adc.v_batt, packet.adc.sense_d, packet.adc.sense_m);
+#else
+       AO_ADC_DUMP(&packet);
+#endif
+}
+
+__code struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0Current ADC" },
+       { 0, NULL },
+};
+
+void
+ao_adc_init(void)
+{
+#ifdef AO_ADC_PINS
+       ADCCFG = AO_ADC_PINS;
+
+#else
+
+#if IGNITE_ON_P2
+       /* TeleMetrum configuration */
+       ADCCFG = ((1 << 0) |    /* acceleration */
+                 (1 << 1) |    /* pressure */
+#if HAS_EXTERNAL_TEMP
+                 (1 << 2) |    /* v0.1 temperature */
+#endif
+                 (1 << 3) |    /* battery voltage */
+                 (1 << 4) |    /* drogue sense */
+                 (1 << 5));    /* main sense */
+#endif
+
+#if IGNITE_ON_P0
+       /* TeleMini configuration */
+       ADCCFG = ((1 << 0) |    /* pressure */
+                 (1 << 1) |    /* drogue sense */
+                 (1 << 2) |    /* main sense */
+                 (1 << 3));    /* battery voltage */
+#endif
+
+#endif /* else AO_ADC_PINS */
+
+       /* enable interrupts */
+       ADCIF = 0;
+       IEN0 |= IEN0_ADCIE;
+       ao_cmd_register(&ao_adc_cmds[0]);
+}
diff --git a/src/cc1111/ao_aes.c b/src/cc1111/ao_aes.c
new file mode 100644 (file)
index 0000000..b1f305e
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#if !HAS_AES
+#error Must define HAS_AES 1
+#endif
+
+__xdata uint8_t ao_aes_mutex;
+__xdata uint8_t        ao_aes_done;
+__xdata uint8_t        ao_aes_dma_in, ao_aes_dma_out;
+__xdata uint8_t ao_aes_dma_in_done, ao_aes_dma_out_done;
+__pdata enum ao_aes_mode ao_aes_current_mode;
+
+void
+ao_aes_isr(void) __interrupt 4
+{
+       S0CON = 0;
+       if (ENCCCS & ENCCCS_RDY) {
+               ao_aes_done = 1;
+               ao_wakeup(&ao_aes_done);
+       }
+}
+
+void
+ao_aes_set_mode(enum ao_aes_mode mode)
+{
+       ao_aes_current_mode = mode;
+}
+
+void
+ao_aes_set_key(__xdata uint8_t *in)
+{
+       ao_dma_set_transfer(ao_aes_dma_in,
+                           in,
+                           &ENCDIXADDR,
+                           AO_AES_LEN,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           DMA_CFG0_TRIGGER_ENC_DW,
+                           DMA_CFG1_SRCINC_1 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_LOW);
+       ao_dma_start(ao_aes_dma_in);
+       ao_aes_done = 0;
+       ENCCCS = ENCCCS_MODE_CBC_MAC |
+               ENCCCS_CMD_LOAD_KEY;
+       ENCCCS |= ENCCCS_START;
+       __critical while (!ao_aes_done)
+               ao_sleep(&ao_aes_done);
+}
+
+void
+ao_aes_zero_iv(void)
+{
+       uint8_t b;
+
+       ENCCCS = ENCCCS_MODE_CBC_MAC | ENCCCS_CMD_LOAD_IV | ENCCCS_START;
+       for (b = 0; b < AO_AES_LEN; b++)
+               ENCDI = 0;
+}
+
+void
+ao_aes_run(__xdata uint8_t *in,
+          __xdata uint8_t *out)
+{
+       uint8_t b;
+       if (in) {
+               ao_dma_set_transfer(ao_aes_dma_in,
+                                   in,
+                                   &ENCDIXADDR,
+                                   AO_AES_LEN,
+                                   DMA_CFG0_WORDSIZE_8 |
+                                   DMA_CFG0_TMODE_SINGLE |
+                                   DMA_CFG0_TRIGGER_ENC_DW,
+                                   DMA_CFG1_SRCINC_1 |
+                                   DMA_CFG1_DESTINC_0 |
+                                   DMA_CFG1_PRIORITY_LOW);
+       }
+       if (out) {
+               ao_dma_set_transfer(ao_aes_dma_out,
+                                   &ENCDOXADDR,
+                                   out,
+                                   AO_AES_LEN,
+                                   DMA_CFG0_WORDSIZE_8 |
+                                   DMA_CFG0_TMODE_SINGLE |
+                                   DMA_CFG0_TRIGGER_ENC_UP,
+                                   DMA_CFG1_SRCINC_0 |
+                                   DMA_CFG1_DESTINC_1 |
+                                   DMA_CFG1_PRIORITY_LOW);
+       }
+       switch (ao_aes_current_mode) {
+       case ao_aes_mode_cbc_mac:
+               if (out)
+                       b = (ENCCCS_MODE_CBC |
+                            ENCCCS_CMD_ENCRYPT);
+               else
+                       b = (ENCCCS_MODE_CBC_MAC |
+                            ENCCCS_CMD_ENCRYPT);
+               break;
+       default:
+               return;
+       }
+       ao_aes_done = 0;
+       if (in)
+               ao_dma_start(ao_aes_dma_in);
+       if (out)
+               ao_dma_start(ao_aes_dma_out);
+       ENCCCS = b;
+       ENCCCS |= ENCCCS_START;
+       if (out) {
+               __critical while (!ao_aes_dma_out_done)
+                       ao_sleep(&ao_aes_dma_out_done);
+       } else {
+               __critical while (!ao_aes_done)
+                       ao_sleep(&ao_aes_done);
+       }
+}
+
+void
+ao_aes_init(void)
+{
+#if DMA_SHARE_AES_RADIO
+       ao_aes_dma_in = ao_radio_dma;
+#else
+       ao_aes_dma_in = ao_dma_alloc(&ao_aes_dma_in_done);
+#endif
+       ao_aes_dma_out = ao_dma_alloc(&ao_aes_dma_out_done);
+       S0CON = 0;
+       ENCIE = 1;
+}
diff --git a/src/cc1111/ao_arch.h b/src/cc1111/ao_arch.h
new file mode 100644 (file)
index 0000000..a97515a
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+/*
+ * CC1111 definitions and code fragments for AltOS
+ */
+
+#ifndef _AO_ARCH_H_
+#define _AO_ARCH_H_
+
+#include "cc1111.h"
+
+/* Convert a __data pointer into an __xdata pointer */
+#define DATA_TO_XDATA(a)       ((void __xdata *) ((uint8_t) (a) | 0xff00))
+
+/* Code and xdata use the same address space */
+#define CODE_TO_XDATA(a)       ((__xdata void *) ((uint16_t) (a)))
+
+/* Pdata lives at the start of xdata */
+#define PDATA_TO_XDATA(a)      ((void __xdata *) ((uint8_t) (a) | 0xf000))
+
+/* Stack runs from above the allocated __data space to 0xfe, which avoids
+ * writing to 0xff as that triggers the stack overflow indicator
+ */
+#define AO_STACK_START 0x90
+#define AO_STACK_END   0xfe
+#define AO_STACK_SIZE  (AO_STACK_END - AO_STACK_START + 1)
+
+#define ao_arch_reboot() do {                                  \
+       WDCTL = WDCTL_EN | WDCTL_MODE_WATCHDOG | WDCTL_INT_64;  \
+       ao_delay(AO_SEC_TO_TICKS(2));                           \
+       } while (0)
+       
+#define ao_arch_nop()  __asm nop __endasm
+#define ao_arch_interrupt(n)   __interrupt n
+
+#define ao_arch_naked_declare  __naked
+#define ao_arch_naked_define   __naked
+
+/* CC1111-specific drivers */
+
+/*
+ * ao_romconfig.c
+ */
+
+#define AO_ROMCONFIG_VERSION   2
+
+#define AO_ROMCONFIG_SYMBOL(a) __code __at(a)
+
+extern AO_ROMCONFIG_SYMBOL(0x00a0) uint16_t ao_romconfig_version;
+extern AO_ROMCONFIG_SYMBOL(0x00a2) uint16_t ao_romconfig_check;
+extern AO_ROMCONFIG_SYMBOL(0x00a4) uint16_t ao_serial_number;
+extern AO_ROMCONFIG_SYMBOL(0x00a6) uint32_t ao_radio_cal;
+
+#ifndef HAS_USB
+#error Please define HAS_USB
+#endif
+
+#define ao_arch_task_members\
+       uint8_t stack_count;            /* amount of saved stack */
+
+/* Initialize stack */
+#define ao_arch_init_stack(task, start) {                      \
+       uint8_t __xdata *stack = task->stack;                   \
+       uint8_t t;                                              \
+       *stack++ = ((uint16_t) start);          /* 0 */         \
+       *stack++ = ((uint16_t) start) >> 8;     /* 1 */         \
+                                                               \
+       /* and the stuff saved by ao_switch */                  \
+       *stack++ = 0;                           /* 2 acc */     \
+       *stack++ = 0x80;                        /* 3 IE */      \
+                                                               \
+       /*  4 DPL                                               \
+        *  5 DPH                                               \
+        *  6 B                                                 \
+        *  7 R2                                                \
+        *  8 R3                                                \
+        *  9 R4                                                \
+        * 10 R5                                                \
+        * 11 R6                                                \
+        * 12 R7                                                \
+        * 13 R0                                                \
+        * 14 R1                                                \
+        * 15 PSW                                               \
+        * 16 BP                                                \
+        */                                                     \
+       for (t = 0; t < 13; t++)                                \
+               *stack++ = 0;                                   \
+       task->stack_count = 17;                                 \
+       }
+
+
+  
+/* Save current context */
+
+#define ao_arch_save_regs()                                            \
+       __asm                                                           \
+       /* Push ACC first, as when restoring the context it must be restored \
+        * last (it is used to set the IE register). */                 \
+       push    ACC                                                     \
+       /* Store the IE register then enable interrupts. */             \
+       push    _IEN0                                                   \
+       setb    _EA                                                     \
+       push    DPL                                                     \
+       push    DPH                                                     \
+       push    b                                                       \
+       push    ar2                                                     \
+       push    ar3                                                     \
+       push    ar4                                                     \
+       push    ar5                                                     \
+       push    ar6                                                     \
+       push    ar7                                                     \
+       push    ar0                                                     \
+       push    ar1                                                     \
+       push    PSW                                                     \
+       __endasm;                                                       \
+       PSW = 0;                                                        \
+       __asm                                                           \
+       push    _bp                                                     \
+       __endasm
+
+#define ao_arch_save_stack() {                                                 \
+               uint8_t stack_len;                                      \
+               __data uint8_t *stack_ptr;                              \
+               __xdata uint8_t *save_ptr;                              \
+               /* Save the current stack */                            \
+               stack_len = SP - (AO_STACK_START - 1);                  \
+               ao_cur_task->stack_count = stack_len;                   \
+               stack_ptr = (uint8_t __data *) AO_STACK_START;          \
+               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;      \
+               do                                                      \
+                       *save_ptr++ = *stack_ptr++;                     \
+               while (--stack_len);                                    \
+       }
+
+#define ao_arch_isr_stack()                                            \
+       /* Empty the stack; might as well let interrupts have the whole thing */ \
+       (SP = AO_STACK_START - 1)
+
+#define ao_arch_cpu_idle()     (PCON = PCON_IDLE)
+
+#define ao_arch_restore_stack() {                                      \
+               uint8_t stack_len;                                      \
+               __data uint8_t *stack_ptr;                              \
+               __xdata uint8_t *save_ptr;                              \
+                                                                       \
+               /* Restore the old stack */                             \
+               stack_len = ao_cur_task->stack_count;                   \
+               SP = AO_STACK_START - 1 + stack_len;                    \
+                                                                       \
+               stack_ptr = (uint8_t __data *) AO_STACK_START;          \
+               save_ptr = (uint8_t __xdata *) ao_cur_task->stack;      \
+               do                                                      \
+                       *stack_ptr++ = *save_ptr++;                     \
+               while (--stack_len);                                    \
+                                                                       \
+               __asm                                                   \
+               pop             _bp                                     \
+               pop             PSW                                     \
+               pop             ar1                                     \
+               pop             ar0                                     \
+               pop             ar7                                     \
+               pop             ar6                                     \
+               pop             ar5                                     \
+               pop             ar4                                     \
+               pop             ar3                                     \
+               pop             ar2                                     \
+               pop             b                                       \
+               pop             DPH                                     \
+               pop             DPL                                     \
+               /* The next byte of the stack is the IE register.  Only the global \
+                  enable bit forms part of the task context.  Pop off the IE then set \
+                  the global enable bit to match that of the stored IE register. */ \
+               pop             ACC                                     \
+               JB              ACC.7,0098$                             \
+               CLR             _EA                                     \
+               LJMP    0099$                                           \
+               0098$:                                                  \
+                       SETB            _EA                             \
+               0099$:                                                  \
+               /* Finally pop off the ACC, which was the first register saved. */ \
+               pop             ACC                                     \
+               ret                                                     \
+               __endasm;                                               \
+}
+
+#define ao_arch_critical(b) __critical { b }
+
+#define AO_DATA_RING   32
+
+/* ao_button.c */
+#ifdef HAS_BUTTON
+void
+ao_p0_isr(void) ao_arch_interrupt(13);
+
+void
+ao_p1_isr(void) ao_arch_interrupt(15);
+
+void
+ao_p2_isr(void);
+
+#define HAS_P2_ISR     1
+
+#endif
+
+void
+ao_button_init(void);
+
+char
+ao_button_get(void) __critical;
+
+void
+ao_button_clear(void) __critical;
+
+/* ao_string.c */
+
+void
+_ao_xmemcpy(__xdata void *dst, __xdata void *src, uint8_t count);
+
+#define ao_xmemcpy(d,s,c) _ao_xmemcpy(d,s,c)
+
+void
+_ao_xmemset(__xdata void *dst, uint8_t value, uint8_t count);
+
+#define ao_xmemset(d,v,c) _ao_xmemset(d,v,c)
+
+int8_t
+_ao_xmemcmp(__xdata void *a, __xdata void *b, uint8_t count);
+
+#define ao_xmemcmp(d,s,c) _ao_xmemcmp((d), (s), (c))
+
+struct ao_serial_speed {
+       uint8_t baud;
+       uint8_t gcr;
+};
+
+extern const __code struct ao_serial_speed ao_serial_speeds[];
+
+/*
+ * ao_dma.c
+ */
+
+/* Allocate a DMA channel. the 'done' parameter will be set when the
+ * dma is finished and will be used to wakeup any waiters
+ */
+
+uint8_t
+ao_dma_alloc(__xdata uint8_t * done);
+
+/* Setup a DMA channel */
+void
+ao_dma_set_transfer(uint8_t id,
+                   void __xdata *srcaddr,
+                   void __xdata *dstaddr,
+                   uint16_t count,
+                   uint8_t cfg0,
+                   uint8_t cfg1);
+
+/* Start a DMA channel */
+void
+ao_dma_start(uint8_t id);
+
+/* Manually trigger a DMA channel */
+void
+ao_dma_trigger(uint8_t id);
+
+/* Abort a running DMA transfer */
+void
+ao_dma_abort(uint8_t id);
+
+/* DMA interrupt routine */
+void
+ao_dma_isr(void) ao_arch_interrupt(8);
+
+/* ao_adc.c */
+
+#if HAS_ADC
+/* The A/D interrupt handler */
+void
+ao_adc_isr(void) ao_arch_interrupt(1);
+#endif
+
+#if HAS_USB
+/* USB interrupt handler */
+void
+ao_usb_isr(void) ao_arch_interrupt(6);
+#endif
+
+#if HAS_SERIAL_0
+void
+ao_serial0_rx_isr(void) ao_arch_interrupt(2);
+
+void
+ao_serial0_tx_isr(void) ao_arch_interrupt(7);
+#endif
+
+#if HAS_SERIAL_1
+void
+ao_serial1_rx_isr(void) ao_arch_interrupt(3);
+
+void
+ao_serial1_tx_isr(void) ao_arch_interrupt(14);
+#endif
+
+#endif /* _AO_ARCH_H_ */
diff --git a/src/cc1111/ao_arch_funcs.h b/src/cc1111/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..8f1cc09
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+/*
+ * ao_spi.c
+ */
+
+extern __xdata uint8_t ao_spi_mutex;
+
+#define AO_SPI_SPEED_FAST      17
+#define AO_SPI_SPEED_200kHz    13
+
+#define ao_spi_set_speed(speed) (U0GCR = (UxGCR_CPOL_NEGATIVE |                \
+                                         UxGCR_CPHA_FIRST_EDGE |       \
+                                         UxGCR_ORDER_MSB |             \
+                                         ((speed) << UxGCR_BAUD_E_SHIFT)))
+
+#define ao_spi_get_slave(bus) do {                     \
+               ao_mutex_get(&ao_spi_mutex);            \
+               ao_spi_set_speed(AO_SPI_SPEED_FAST);    \
+       } while (0)
+
+#define ao_spi_put_slave(bus) do {             \
+               ao_mutex_put(&ao_spi_mutex);    \
+       } while (0)
+
+#define ao_spi_get_mask(reg,mask,bus,speed) do {       \
+               ao_mutex_get(&ao_spi_mutex);            \
+               ao_spi_set_speed(speed);                \
+               (reg) &= ~(mask);                       \
+       } while (0)
+
+#define ao_spi_put_mask(reg,mask,bus) do {             \
+       (reg) |= (mask); \
+       ao_mutex_put(&ao_spi_mutex); \
+       } while (0)
+
+
+#define ao_spi_get_bit(reg,bit,pin,bus,speed) do {     \
+               ao_mutex_get(&ao_spi_mutex);    \
+               ao_spi_set_speed(speed);        \
+               pin = 0;                        \
+       } while (0)
+
+#define ao_spi_put_bit(reg,bit,pin,bus) do {   \
+               pin = 1;                        \
+               ao_mutex_put(&ao_spi_mutex);    \
+       } while (0)
+
+
+/*
+ * The SPI mutex must be held to call either of these
+ * functions -- this mutex covers the entire SPI operation,
+ * from chip select low to chip select high
+ */
+
+void
+ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant;
+
+void
+ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant;
+
+#define ao_spi_send(block, len, bus) ao_spi_send_bus(block, len)
+#define ao_spi_recv(block, len, bus) ao_spi_recv_bus(block, len)
+
+#if AO_SPI_SLAVE
+void
+ao_spi_send_wait(void);
+
+void
+ao_spi_recv_wait(void);
+#endif
+
+void
+ao_spi_init(void);
+
+#define ao_spi_init_cs(port, mask) do {                \
+               SPI_CS_PORT |= mask;            \
+               SPI_CS_DIR |= mask;             \
+               SPI_CS_SEL &= ~mask;            \
+       } while (0)
+
+#define cc1111_enable_output(port,dir,sel,pin,bit,v) do {      \
+               pin = v;                                        \
+               dir |= (1 << bit);                              \
+               sel &= ~(1 << bit);                             \
+       } while (0)
+
+#define disable_unreachable    _Pragma("disable_warning 126")
+
+#define token_paster(x,y)      x ## y
+#define token_evaluator(x,y)   token_paster(x,y)
+#define ao_enable_output(port,bit,pin,v) cc1111_enable_output(port,token_evaluator(port,DIR), token_evaluator(port,SEL), pin, bit, v)
+#define ao_gpio_set(port, bit, pin, v) ((pin) = (v))
diff --git a/src/cc1111/ao_battery.c b/src/cc1111/ao_battery.c
new file mode 100644 (file)
index 0000000..b9845fb
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+static __data union {
+       uint8_t d[2];
+       int16_t v;
+} ao_battery_value;
+
+void
+ao_battery_isr(void) ao_arch_interrupt(1)
+{
+       ao_battery_value.d[0] = ADCL;
+       ao_battery_value.d[1] = ADCH;
+       ao_wakeup(DATA_TO_XDATA(&ao_battery_value));
+}
+
+uint16_t
+ao_battery_get(void) 
+{
+       ao_arch_critical(
+               ADCCON3 = ADCCON3_EREF_VDD | ADCCON3_EDIV_512 | BATTERY_PIN;
+               ao_sleep(DATA_TO_XDATA(&ao_battery_value));
+               );
+       return (uint16_t) ((int32_t) ao_battery_value.v * (int32_t) 4950 >> 15);
+}
+
+static void
+ao_battery_show(void)
+{
+       printf("Battery: %u mV\n", ao_battery_get());
+}
+
+__code struct ao_cmds ao_battery_cmds[] = {
+       { ao_battery_show,      "B\0Show battery voltage" },
+       { 0, NULL },
+};
+
+void
+ao_battery_init(void)
+{
+       ADCCFG = (1 << BATTERY_PIN);
+       ADCIF = 0;
+       IEN0 |= IEN0_ADCIE;
+       ao_cmd_register(&ao_battery_cmds[0]);
+}
diff --git a/src/cc1111/ao_beep.c b/src/cc1111/ao_beep.c
new file mode 100644 (file)
index 0000000..3642f4c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+ao_beep(uint8_t beep)
+{
+       if (beep == 0) {
+               P2_0 = 0;
+               P2SEL = (P2SEL & ~P2SEL_SELP2_0_MASK) | P2SEL_SELP2_0_GPIO;
+               T4CTL = 0;
+       } else {
+               P2SEL = (P2SEL & ~P2SEL_SELP2_0_MASK) | P2SEL_SELP2_0_PERIPHERAL;
+               T4CC0 = beep;
+               T4CTL = TxCTL_DIV_32 | TxCTL_MODE_MODULO | TxCTL_START;
+       }
+}
+
+void
+ao_beep_for(uint8_t beep, uint16_t ticks) __reentrant
+{
+       ao_beep(beep);
+       ao_delay(ticks);
+       ao_beep(0);
+}
+
+void
+ao_beep_init(void)
+{
+       /* Our beeper is on P2_0, which is hooked to timer 4 using
+        * configuration alternative 2
+        */
+       P2_0 = 0;
+       P2SEL = (P2SEL & ~P2SEL_SELP2_0_MASK) | P2SEL_SELP2_0_GPIO;
+       PERCFG = (PERCFG & ~PERCFG_T4CFG_ALT_MASK) | PERCFG_T4CFG_ALT_2;
+       T4CCTL0 = TxCCTLy_CMP_TOGGLE|TxCCTLy_CMP_MODE_ENABLE;
+}
diff --git a/src/cc1111/ao_button.c b/src/cc1111/ao_button.c
new file mode 100644 (file)
index 0000000..69f3475
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+volatile __xdata struct ao_fifo ao_button_fifo;
+
+static __code struct {
+       uint8_t mask;
+       uint8_t reg;
+} ao_buttons[] = {
+#ifdef BUTTON_1_MASK
+       { BUTTON_1_MASK, BUTTON_1_REG },
+#endif
+#ifdef BUTTON_2_MASK
+       { BUTTON_2_MASK, BUTTON_2_REG },
+#endif
+#ifdef BUTTON_3_MASK
+       { BUTTON_3_MASK, BUTTON_3_REG },
+#endif
+};
+
+#define NUM_BUTTONS    ((sizeof ao_buttons) / sizeof (ao_buttons[0]))
+
+static __xdata uint16_t ao_button_tick[NUM_BUTTONS];
+
+static void
+ao_button_insert(char n)
+{
+       uint16_t        now = ao_time();
+       if ((now - ao_button_tick[n]) > 20) {
+               ao_button_tick[n] = now;
+               ao_fifo_insert(ao_button_fifo, n);
+               ao_wakeup(&ao_button_fifo);
+       }
+}
+
+static void
+ao_button_isr(uint8_t flag, uint8_t reg)
+{
+       uint8_t b;
+
+       for (b = 0; b < NUM_BUTTONS; b++)
+               if (ao_buttons[b].reg == reg && (ao_buttons[b].mask & flag))
+                       ao_button_insert(b + 1);
+}
+
+static uint8_t
+ao_button_mask(uint8_t reg)
+{
+       uint8_t b;
+       uint8_t mask = 0;
+
+       for (b = 0; b < NUM_BUTTONS; b++)
+               if (ao_buttons[b].reg == reg)
+                       mask |= ao_buttons[b].mask;
+       return mask;
+}
+
+char
+ao_button_get(void) __critical
+{
+       char    b;
+
+       while (ao_fifo_empty(ao_button_fifo))
+               if (ao_sleep(&ao_button_fifo))
+                       return 0;
+       ao_fifo_remove(ao_button_fifo, b);
+       return b;
+}
+
+void
+ao_button_clear(void) __critical
+{
+       char b;
+
+       while (!ao_fifo_empty(ao_button_fifo))
+               ao_fifo_remove(ao_button_fifo, b);
+}
+
+void
+ao_p0_isr(void) ao_arch_interrupt(13)
+{
+       P0IF = 0;
+       ao_button_isr(P0IFG, 0);
+       P0IFG = 0;
+}
+
+void
+ao_p1_isr(void) ao_arch_interrupt(15)
+{
+       P1IF = 0;
+       ao_button_isr(P1IFG, 1);
+       P1IFG = 0;
+}
+
+/* Shared with USB */
+void
+ao_p2_isr(void)
+{
+       ao_button_isr(P2IFG, 2);
+       P2IFG = 0;
+}
+
+void
+ao_button_init(void)
+{
+       uint8_t mask;
+
+       /* Pins are configured as inputs with pull-up by default */
+
+       /* Enable interrupts for P0 inputs */
+       mask = ao_button_mask(0);
+       if (mask) {
+               if (mask & 0x0f)
+                       PICTL |= PICTL_P0IENL;
+               if (mask & 0xf0)
+                       PICTL |= PICTL_P0IENH;
+               P0IFG = 0;
+               P0IF = 0;
+               IEN1 |= IEN1_P0IE;
+               PICTL |= PICTL_P0ICON;
+       }
+
+       /* Enable interrupts for P1 inputs */
+       mask = ao_button_mask(1);
+       if (mask) {
+               P1IEN |= mask;
+               P1IFG = 0;
+               P1IF = 0;
+               IEN2 |= IEN2_P1IE;
+               PICTL |= PICTL_P1ICON;
+       }
+
+       /* Enable interrupts for P2 inputs */
+       mask = ao_button_mask(2);
+       if (mask) {
+               PICTL |= PICTL_P2IEN;
+               P2IFG = 0;
+               P2IF = 0;
+               IEN2 |= IEN2_P2IE;
+               PICTL |= PICTL_P2ICON;
+       }
+}
diff --git a/src/cc1111/ao_dbg.c b/src/cc1111/ao_dbg.c
new file mode 100644 (file)
index 0000000..847b5aa
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_pins.h"
+
+static void
+ao_dbg_send_bits(uint8_t msk, uint8_t val) __reentrant
+{
+       DBG_PORT = (DBG_PORT & ~msk) | (val & msk);
+       __asm
+               nop
+               nop
+       __endasm;
+}
+
+void
+ao_dbg_send_byte(uint8_t byte)
+{
+       __pdata uint8_t b, d;
+
+       DBG_PORT |= DBG_DATA;
+       DBG_PORT_DIR |= DBG_DATA;
+       for (b = 0; b < 8; b++) {
+               d = 0;
+               if (byte & 0x80)
+                       d = DBG_DATA;
+               byte <<= 1;
+               ao_dbg_send_bits(DBG_CLOCK|DBG_DATA, DBG_CLOCK|d);
+               ao_dbg_send_bits(DBG_CLOCK|DBG_DATA,     0    |d);
+       }
+       DBG_PORT_DIR &= ~DBG_DATA;
+}
+
+uint8_t
+ao_dbg_recv_byte(void)
+{
+       __pdata uint8_t byte, b;
+
+       byte = 0;
+       for (b = 0; b < 8; b++) {
+               byte = byte << 1;
+               ao_dbg_send_bits(DBG_CLOCK, DBG_CLOCK);
+               if (DBG_DATA_PIN)
+                       byte |= 1;
+               ao_dbg_send_bits(DBG_CLOCK, 0);
+       }
+       return byte;
+}
+
+/* 8051 instructions
+ */
+#define NOP                    0x00
+#define MOV_direct_data                0x75
+#define LJMP                   0x02
+#define MOV_Rn_data(n)         (0x78 | (n))
+#define DJNZ_Rn_rel(n)         (0xd8 | (n))
+#define MOV_A_direct           0xe5
+#define MOV_direct1_direct2    0x85
+#define MOV_direct_A           0xf5
+#define MOV_DPTR_data16                0x90
+#define MOV_A_data             0x74
+#define MOVX_atDPTR_A          0xf0
+#define MOVX_A_atDPTR          0xe0
+#define INC_DPTR               0xa3
+#define TRAP                   0xa5
+#define SJMP                   0x80
+#define JB                     0x20
+
+#define DEBUG_INSTR(l)         (0x54 | (l))
+
+#define SFR_PSW                        0xD0
+#define SFR_DPL0               0x82
+#define SFR_DPH0               0x83
+#define SFR_DPL1               0x84
+#define SFR_DPH1               0x85
+
+__pdata uint8_t        save_acc;
+__pdata uint8_t save_psw;
+__pdata uint8_t save_dpl0;
+__pdata uint8_t save_dph0;
+__pdata uint8_t save_dpl1;
+__pdata uint8_t save_dph1;
+
+static uint8_t
+ao_dbg_inst1(uint8_t a) __reentrant
+{
+       ao_dbg_send_byte(DEBUG_INSTR(1));
+       ao_dbg_send_byte(a);
+       return ao_dbg_recv_byte();
+}
+
+static uint8_t
+ao_dbg_inst2(uint8_t a, uint8_t b) __reentrant
+{
+       ao_dbg_send_byte(DEBUG_INSTR(2));
+       ao_dbg_send_byte(a);
+       ao_dbg_send_byte(b);
+       return ao_dbg_recv_byte();
+}
+
+static uint8_t
+ao_dbg_inst3(uint8_t a, uint8_t b, uint8_t c) __reentrant
+{
+       ao_dbg_send_byte(DEBUG_INSTR(3));
+       ao_dbg_send_byte(a);
+       ao_dbg_send_byte(b);
+       ao_dbg_send_byte(c);
+       return ao_dbg_recv_byte();
+}
+
+void
+ao_dbg_start_transfer(uint16_t addr)
+{
+       save_acc  = ao_dbg_inst1(NOP);
+       save_psw  = ao_dbg_inst2(MOV_A_direct, SFR_PSW);
+       save_dpl0 = ao_dbg_inst2(MOV_A_direct, SFR_DPL0);
+       save_dph0 = ao_dbg_inst2(MOV_A_direct, SFR_DPH0);
+       save_dpl1 = ao_dbg_inst2(MOV_A_direct, SFR_DPL1);
+       save_dph1 = ao_dbg_inst2(MOV_A_direct, SFR_DPH1);
+       ao_dbg_inst3(MOV_DPTR_data16, addr >> 8, addr);
+}
+
+void
+ao_dbg_end_transfer(void)
+{
+       ao_dbg_inst3(MOV_direct_data, SFR_DPL0, save_dpl0);
+       ao_dbg_inst3(MOV_direct_data, SFR_DPH0, save_dph0);
+       ao_dbg_inst3(MOV_direct_data, SFR_DPL1, save_dpl1);
+       ao_dbg_inst3(MOV_direct_data, SFR_DPH1, save_dph1);
+       ao_dbg_inst3(MOV_direct_data, SFR_PSW, save_psw);
+       ao_dbg_inst2(MOV_A_data, save_acc);
+}
+
+void
+ao_dbg_write_byte(uint8_t byte)
+{
+       ao_dbg_inst2(MOV_A_data, byte);
+       ao_dbg_inst1(MOVX_atDPTR_A);
+       ao_dbg_inst1(INC_DPTR);
+}
+
+uint8_t
+ao_dbg_read_byte(void)
+{
+       ao_dbg_inst1(MOVX_A_atDPTR);
+       return ao_dbg_inst1(INC_DPTR);
+}
+
+static void
+ao_dbg_set_pins(void)
+{
+       /* Make the DBG pins GPIOs. On TeleMetrum, this will
+        * disable the SPI link, so don't expect SPI to work after
+        * using the debugger.
+        */
+       DBG_PORT_SEL &= ~(DBG_CLOCK|DBG_DATA|DBG_RESET_N);
+
+       /* make DBG_DATA tri-state */
+       DBG_PORT_INP |= DBG_DATA;
+
+       /* Raise RESET_N and CLOCK */
+       DBG_PORT |= DBG_RESET_N | DBG_CLOCK;
+
+       /* RESET_N and CLOCK are outputs now */
+       DBG_PORT_DIR |= DBG_RESET_N | DBG_CLOCK;
+       DBG_PORT_DIR &= ~DBG_DATA;
+}
+
+static void
+ao_dbg_long_delay(void)
+{
+       uint8_t n;
+
+       for (n = 0; n < 20; n++)
+               __asm nop __endasm;
+}
+
+#define AO_RESET_LOW_DELAY     AO_MS_TO_TICKS(100)
+#define AO_RESET_HIGH_DELAY    AO_MS_TO_TICKS(100)
+
+void
+ao_dbg_debug_mode(void)
+{
+       ao_dbg_set_pins();
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|DBG_RESET_N);
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N,     0    |DBG_DATA|    0    );
+       ao_delay(AO_RESET_LOW_DELAY);
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N,     0    |DBG_DATA|    0    );
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N,     0    |DBG_DATA|DBG_RESET_N);
+       ao_delay(AO_RESET_HIGH_DELAY);
+}
+
+void
+ao_dbg_reset(void)
+{
+       ao_dbg_set_pins();
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|DBG_RESET_N);
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
+       ao_delay(AO_RESET_LOW_DELAY);
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|    0    );
+       ao_dbg_long_delay();
+       ao_dbg_send_bits(DBG_CLOCK|DBG_DATA|DBG_RESET_N, DBG_CLOCK|DBG_DATA|DBG_RESET_N);
+       ao_delay(AO_RESET_HIGH_DELAY);
+}
+
+static void
+debug_enable(void)
+{
+       ao_dbg_debug_mode();
+}
+
+static void
+debug_reset(void)
+{
+       ao_dbg_reset();
+}
+
+static void
+debug_put(void)
+{
+       for (;;) {
+               ao_cmd_white ();
+               if (ao_cmd_lex_c == '\n')
+                       break;
+               ao_cmd_hex();
+               if (ao_cmd_status != ao_cmd_success)
+                       break;
+               ao_dbg_send_byte(ao_cmd_lex_i);
+       }
+}
+
+static void
+debug_get(void)
+{
+       __pdata uint16_t count;
+       __pdata uint16_t i;
+       __pdata uint8_t byte;
+       ao_cmd_hex();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       count = ao_cmd_lex_i;
+       if (count > 256) {
+               ao_cmd_status = ao_cmd_syntax_error;
+               return;
+       }
+       for (i = 0; i < count; i++) {
+               if (i && (i & 7) == 0)
+                       putchar('\n');
+               byte = ao_dbg_recv_byte();
+               ao_cmd_put8(byte);
+               putchar(' ');
+       }
+       putchar('\n');
+}
+
+static uint8_t
+getnibble(void)
+{
+       __pdata char    c;
+
+       c = getchar();
+       if ('0' <= c && c <= '9')
+               return c - '0';
+       if ('a' <= c && c <= 'f')
+               return c - ('a' - 10);
+       if ('A' <= c && c <= 'F')
+               return c - ('A' - 10);
+       ao_cmd_status = ao_cmd_lex_error;
+       return 0;
+}
+
+static void
+debug_input(void)
+{
+       __pdata uint16_t count;
+       __pdata uint16_t addr;
+       __pdata uint8_t b;
+       __pdata uint8_t i;
+
+       ao_cmd_hex();
+       count = ao_cmd_lex_i;
+       ao_cmd_hex();
+       addr = ao_cmd_lex_i;
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       ao_dbg_start_transfer(addr);
+       i = 0;
+       while (count--) {
+               if (!(i++ & 7))
+                       putchar('\n');
+               b = ao_dbg_read_byte();
+               ao_cmd_put8(b);
+       }
+       ao_dbg_end_transfer();
+       putchar('\n');
+}
+
+static void
+debug_output(void)
+{
+       __pdata uint16_t count;
+       __pdata uint16_t addr;
+       __pdata uint8_t b;
+
+       ao_cmd_hex();
+       count = ao_cmd_lex_i;
+       ao_cmd_hex();
+       addr = ao_cmd_lex_i;
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       ao_dbg_start_transfer(addr);
+       while (count--) {
+               b = getnibble() << 4;
+               b |= getnibble();
+               if (ao_cmd_status != ao_cmd_success)
+                       return;
+               ao_dbg_write_byte(b);
+       }
+       ao_dbg_end_transfer();
+}
+
+__code struct ao_cmds ao_dbg_cmds[7] = {
+       { debug_enable, "D\0Enable debug" },
+       { debug_get,    "G <count>\0Get data" },
+       { debug_input,  "I <count> <addr>\0Input <count> at <addr>" },
+       { debug_output, "O <count> <addr>\0Output <count> at <addr>" },
+       { debug_put,    "P <byte> ...\0Put data" },
+       { debug_reset,  "R\0Reset" },
+       { 0, NULL },
+};
+
+void
+ao_dbg_init(void)
+{
+       ao_cmd_register(&ao_dbg_cmds[0]);
+}
diff --git a/src/cc1111/ao_dma.c b/src/cc1111/ao_dma.c
new file mode 100644 (file)
index 0000000..8779ddf
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#define NUM_DMA        5
+
+/*
+ * The config address for DMA0 is programmed
+ * separately from that of DMA1-4, but for simplicity,
+ * we make them all contiguous.
+ */
+
+static __xdata struct cc_dma_channel ao_dma_config[NUM_DMA];
+static __xdata uint8_t * __xdata ao_dma_done[NUM_DMA];
+static __data uint8_t ao_next_dma;
+
+uint8_t
+ao_dma_alloc(__xdata uint8_t *done)
+{
+       uint8_t id;
+
+       if (ao_next_dma == NUM_DMA)
+               ao_panic(AO_PANIC_DMA);
+       id = ao_next_dma++;
+       ao_dma_done[id] = done;
+
+       /* When the first dma object is allocated, set up the DMA
+        * controller
+        */
+       if (id == 0) {
+               DMAIRQ = 0;
+               DMAIF = 0;
+               IEN1 |= IEN1_DMAIE;
+               DMA0CFGH = ((uint16_t) (&ao_dma_config[0])) >> 8;
+               DMA0CFGL = ((uint16_t) (&ao_dma_config[0]));
+               DMA1CFGH = ((uint16_t) (&ao_dma_config[1])) >> 8;
+               DMA1CFGL = ((uint16_t) (&ao_dma_config[1]));
+       }
+
+       return id;
+}
+
+void
+ao_dma_set_transfer(uint8_t id,
+                   void __xdata *srcaddr,
+                   void __xdata *dstaddr,
+                   uint16_t count,
+                   uint8_t cfg0,
+                   uint8_t cfg1)
+{
+       if (DMAARM & (1 << id))
+               ao_panic(AO_PANIC_DMA);
+       ao_dma_config[id].src_high = ((uint16_t) srcaddr) >> 8;
+       ao_dma_config[id].src_low = ((uint16_t) srcaddr);
+       ao_dma_config[id].dst_high = ((uint16_t) dstaddr) >> 8;
+       ao_dma_config[id].dst_low = ((uint16_t) dstaddr);
+       ao_dma_config[id].len_high = count >> 8;
+       ao_dma_config[id].len_low = count;
+       ao_dma_config[id].cfg0 = cfg0;
+       ao_dma_config[id].cfg1 = cfg1 | DMA_CFG1_IRQMASK;
+}
+
+#define nop()  __asm nop __endasm;
+
+void
+ao_dma_start(uint8_t id)
+{
+       uint8_t mask = (1 << id);
+       DMAIRQ &= ~mask;
+       if (DMAARM & mask) {
+               DMAARM = 0x80 | mask;
+               nop(); nop(); nop(); nop();
+               nop(); nop(); nop(); nop();
+       }
+       *(ao_dma_done[id]) = 0;
+       DMAARM = mask;
+       nop(); nop(); nop(); nop();
+       nop(); nop(); nop(); nop();
+       nop();
+}
+
+void
+ao_dma_trigger(uint8_t id)
+{
+       DMAREQ |= (1 << id);
+}
+
+void
+ao_dma_abort(uint8_t id)
+{
+       uint8_t mask = (1 << id);
+       DMAARM = 0x80 | mask;
+       DMAIRQ &= ~mask;
+}
+
+void
+ao_dma_isr(void) __interrupt 8
+{
+       uint8_t id, mask;
+
+       /* Find the first DMA channel which is done */
+       mask = 1;
+       for (id = 0; id < ao_next_dma; id++) {
+               if (DMAIRQ & mask) {
+                       /* Clear CPU interrupt flag */
+                       DMAIF = 0;
+                       /* Clear the completed ID */
+                       DMAIRQ = ~mask;
+                       *(ao_dma_done[id]) = 1;
+                       ao_wakeup(ao_dma_done[id]);
+                       break;
+               }
+               mask <<= 1;
+       }
+}
diff --git a/src/cc1111/ao_intflash.c b/src/cc1111/ao_intflash.c
new file mode 100644 (file)
index 0000000..632e2a8
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2011  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.
+ */
+
+#include "ao.h"
+#include "cc1111.h"
+
+#define ENDOFCODE  (CODESIZE)
+#define AO_INTFLASH_BLOCK      1024
+#define AO_INTFLASH_BLOCKS     ((0x8000 - ENDOFCODE)/AO_INTFLASH_BLOCK)
+#define AO_INTFLASH_SIZE       (AO_INTFLASH_BLOCK * AO_INTFLASH_BLOCKS)
+#define AO_INTFLASH_LOCATION   (0x8000 - AO_INTFLASH_SIZE)
+
+/*
+ *       21000 * 24e6
+ * FWT = ------------
+ *           16e9
+ *
+ *     = 31.5
+ *
+ * Round up and use 32
+ */
+
+#define FLASH_TIMING   0x20
+
+#if AO_INTFLASH_BLOCKS < 2
+#error "Too few pages"
+#endif
+
+#if AO_INFTLASH_LOCATION % 1024 != 0
+#error "Pages aren't aligned properly"
+#endif
+
+__xdata __at(AO_INTFLASH_LOCATION) uint8_t ao_intflash[AO_INTFLASH_SIZE];
+
+/* Total bytes of available storage */
+__pdata uint32_t       ao_storage_total = sizeof(ao_intflash);
+
+/* Block size - device is erased in these units. */
+__pdata uint32_t       ao_storage_block = AO_INTFLASH_BLOCK;
+
+/* Byte offset of config block. Will be ao_storage_block bytes long */
+__pdata uint32_t       ao_storage_config = sizeof(ao_intflash) - AO_INTFLASH_BLOCK;
+
+/* Storage unit size - device reads and writes must be within blocks of this size. */
+__pdata uint16_t       ao_storage_unit = AO_INTFLASH_BLOCK;
+
+__xdata static uint8_t  ao_intflash_dma_done;
+static uint8_t ao_intflash_dma;
+
+/*
+ * The internal flash chip is arranged in 1kB sectors; the
+ * chip cannot erase in units smaller than that.
+ *
+ * Writing happens in units of 2 bytes and
+ * can only change bits from 1 to 0. So, you can rewrite
+ * the same contents, or append to an existing page easily enough
+ */
+
+/*
+ * Erase the specified sector
+ */
+uint8_t
+ao_storage_erase(uint32_t pos) __reentrant
+{
+       uint16_t addr;
+
+       if (pos >= ao_storage_total || pos + ao_storage_block > ao_storage_total)
+               return 0;
+
+       addr = ((uint16_t)(ao_intflash + pos) >> 1);
+
+       FADDRH = addr >> 8;
+       FADDRL = addr;
+
+       __critical {
+               _asm
+               .even
+               orl _FCTL, #FCTL_ERASE;         ; FCTL |=  FCTL_ERASE
+               nop                             ; Required, see datasheet.
+               _endasm;
+       }
+
+       return 1;
+}
+
+/*
+ * Write to flash
+ */
+
+static void
+ao_intflash_write_aligned(uint16_t pos, __xdata void *d, uint16_t len) __reentrant
+{
+       pos = ((uint16_t) ao_intflash + pos) >> 1;
+
+       ao_dma_set_transfer(ao_intflash_dma,
+                           d,
+                           &FWDATAXADDR,
+                           len,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           DMA_CFG0_TRIGGER_FLASH,
+                           DMA_CFG1_SRCINC_1 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_HIGH);
+
+       FADDRH = pos >> 8;
+       FADDRL = pos;
+
+       ao_dma_start(ao_intflash_dma);
+
+       __critical {
+               _asm
+               .even
+               orl _FCTL, #FCTL_WRITE;         ; FCTL |=  FCTL_WRITE
+               nop
+               _endasm;
+       }
+}
+
+static void
+ao_intflash_write_byte(uint16_t pos, uint8_t byte) __reentrant
+{
+       static __xdata uint8_t b[2];
+
+       if (pos & 1) {
+               b[0] = 0xff;
+               b[1] = byte;
+       } else {
+               b[0] = byte;
+               b[1] = 0xff;
+       }
+       ao_intflash_write_aligned(pos, b, 2);
+}
+
+uint8_t
+ao_storage_device_write(uint32_t pos32, __xdata void *v, uint16_t len) __reentrant
+{
+       uint16_t pos = pos32;
+       __xdata uint8_t *d = v;
+       uint8_t oddlen;
+
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+       if (len == 0)
+               return 1;
+
+       if (pos & 1) {
+               ao_intflash_write_byte(pos++, *d++);
+               len--;
+       }
+       oddlen = len & 1;
+       len -= oddlen;
+       if (len)
+               ao_intflash_write_aligned(pos, d, len);
+       if (oddlen)
+               ao_intflash_write_byte(pos + len, d[len]);
+
+       return 1;
+}
+
+/*
+ * Read from flash
+ */
+uint8_t
+ao_storage_device_read(uint32_t pos, __xdata void *d, uint16_t len) __reentrant
+{
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+       ao_xmemcpy(d, ao_intflash+pos, len);
+       return 1;
+}
+
+void
+ao_storage_flush(void) __reentrant
+{
+}
+
+void
+ao_storage_setup(void)
+{
+}
+
+void
+ao_storage_device_info(void) __reentrant
+{
+       printf ("Using internal flash, starting at 0x%04x\n", AO_INTFLASH_LOCATION);
+}
+
+void
+ao_storage_device_init(void)
+{
+       ao_intflash_dma = ao_dma_alloc(&ao_intflash_dma_done);
+
+       FWT = FLASH_TIMING;
+}
diff --git a/src/cc1111/ao_launch.c b/src/cc1111/ao_launch.c
new file mode 100644 (file)
index 0000000..420f756
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_radio_cmac.h>
+
+__xdata uint16_t ao_launch_ignite;
+
+#if 0
+#define PRINTD(...) printf(__VA_ARGS__)
+#else
+#define PRINTD(...) 
+#endif
+
+static void
+ao_launch_run(void)
+{
+       for (;;) {
+               while (!ao_launch_ignite)
+                       ao_sleep(&ao_launch_ignite);
+               ao_ignition[ao_igniter_drogue].firing = 1;
+               ao_ignition[ao_igniter_main].firing = 1;
+               AO_IGNITER_DIR |= AO_IGNITER_DROGUE_BIT | AO_IGNITER_MAIN_BIT;
+               AO_IGNITER_DROGUE = 1;
+               while (ao_launch_ignite) {
+                       ao_launch_ignite = 0;
+                       ao_delay(AO_MS_TO_TICKS(500));
+               }
+               AO_IGNITER_DROGUE = 0;
+               ao_ignition[ao_igniter_drogue].firing = 0;
+               ao_ignition[ao_igniter_main].firing = 0;
+       }
+}
+
+static void
+ao_launch_status(void)
+{
+       uint8_t i;
+       for (;;) {
+               ao_delay(AO_SEC_TO_TICKS(1));
+               if (ao_igniter_status(ao_igniter_drogue) == ao_igniter_ready) {
+                       if (ao_igniter_status(ao_igniter_main) == ao_igniter_ready) {
+                               for (i = 0; i < 5; i++) {
+                                       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(50));
+                                       ao_delay(AO_MS_TO_TICKS(100));
+                               }
+                       } else {
+                               ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+                       }
+               }
+       }
+}
+
+static __pdata uint8_t ao_launch_armed;
+static __pdata uint16_t        ao_launch_arm_time;
+
+static void
+ao_launch(void)
+{
+       static __xdata struct ao_launch_command command;
+       static __xdata struct ao_launch_query   query;
+       int16_t time_difference;
+
+       ao_led_off(AO_LED_RED);
+       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+       for (;;) {
+               flush();
+               if (ao_radio_cmac_recv(&command, sizeof (command), 0) != AO_RADIO_CMAC_OK)
+                       continue;
+               
+               PRINTD ("tick %d serial %d cmd %d channel %d\n",
+                       command.tick, command.serial, command.cmd, command.channel);
+
+               switch (command.cmd) {
+               case AO_LAUNCH_QUERY:
+                       if (command.serial != ao_serial_number) {
+                               PRINTD ("serial number mismatch\n");
+                               break;
+                       }
+
+                       if (command.channel == 0) {
+                               query.valid = 1;
+                               query.arm_status = ao_igniter_status(ao_igniter_drogue);
+                               query.igniter_status = ao_igniter_status(ao_igniter_main);
+                       } else {
+                               query.valid = 0;
+                       }
+                       query.tick = ao_time();
+                       query.serial = ao_serial_number;
+                       query.channel = command.channel;
+                       PRINTD ("query tick %d serial %d channel %d valid %d arm %d igniter %d\n",
+                               query.tick, query.serial, query.channel, query.valid, query.arm_status,
+                               query.igniter_status);
+                       ao_radio_cmac_send(&query, sizeof (query));
+                       break;
+               case AO_LAUNCH_ARM:
+                       if (command.serial != ao_serial_number) {
+                               PRINTD ("serial number mismatch\n");
+                               break;
+                       }
+
+                       if (command.channel != 0)
+                               break;
+                       time_difference = command.tick - ao_time();
+                       PRINTD ("arm tick %d local tick %d\n", command.tick, ao_time());
+                       if (time_difference < 0)
+                               time_difference = -time_difference;
+                       if (time_difference > 10) {
+                               PRINTD ("time difference too large %d\n", time_difference);
+                               break;
+                       }
+                       PRINTD ("armed\n");
+                       ao_launch_armed = 1;
+                       ao_launch_arm_time = ao_time();
+                       break;
+               case AO_LAUNCH_FIRE:
+                       if (!ao_launch_armed) {
+                               PRINTD ("not armed\n");
+                               break;
+                       }
+                       if ((uint16_t) (ao_time() - ao_launch_arm_time) > AO_SEC_TO_TICKS(20)) {
+                               PRINTD ("late launch arm_time %d time %d\n",
+                                       ao_launch_arm_time, ao_time());
+                               break;
+                       }
+                       time_difference = command.tick - ao_time();
+                       if (time_difference < 0)
+                               time_difference = -time_difference;
+                       if (time_difference > 10) {
+                               PRINTD ("time different too large %d\n", time_difference);
+                               break;
+                       }
+                       PRINTD ("ignite\n");
+                       ao_launch_ignite = 1;
+                       ao_wakeup(&ao_launch_ignite);
+                       break;
+               }
+       }
+}
+
+void
+ao_launch_test(void)
+{
+       switch (ao_igniter_status(ao_igniter_drogue)) {
+       case ao_igniter_ready:
+       case ao_igniter_active:
+               printf ("Armed: ");
+               switch (ao_igniter_status(ao_igniter_main)) {
+               default:
+                       printf("unknown status\n");
+                       break;
+               case ao_igniter_ready:
+                       printf("igniter good\n");
+                       break;
+               case ao_igniter_open:
+                       printf("igniter bad\n");
+                       break;
+               }
+               break;
+       default:
+               printf("Disarmed\n");
+       }
+}
+
+void
+ao_launch_manual(void)
+{
+       ao_cmd_white();
+       if (!ao_match_word("DoIt"))
+               return;
+       ao_cmd_white();
+       ao_launch_ignite = 1;
+       ao_wakeup(&ao_launch_ignite);
+}
+
+static __xdata struct ao_task ao_launch_task;
+static __xdata struct ao_task ao_launch_ignite_task;
+static __xdata struct ao_task ao_launch_status_task;
+
+__code struct ao_cmds ao_launch_cmds[] = {
+       { ao_launch_test,       "t\0Test launch continuity" },
+       { ao_launch_manual,     "i <key>\0Fire igniter. <key> is doit with D&I" },
+       { 0, NULL }
+};
+
+void
+ao_launch_init(void)
+{
+       AO_IGNITER_DROGUE = 0;
+       AO_IGNITER_MAIN = 0;
+       AO_IGNITER_DIR |= AO_IGNITER_DROGUE_BIT | AO_IGNITER_MAIN_BIT;
+       ao_cmd_register(&ao_launch_cmds[0]);
+       ao_add_task(&ao_launch_task, ao_launch, "launch listener");
+       ao_add_task(&ao_launch_ignite_task, ao_launch_run, "launch igniter");
+       ao_add_task(&ao_launch_status_task, ao_launch_status, "launch status");
+}
diff --git a/src/cc1111/ao_lcd_port.c b/src/cc1111/ao_lcd_port.c
new file mode 100644 (file)
index 0000000..e61b1a6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static void
+ao_lcd_port_delay(void)
+{
+       uint8_t i;
+
+       for (i = 0; i < 100; i++)
+               ao_arch_nop();
+}
+
+void
+ao_lcd_port_put_nibble(uint8_t rs, uint8_t nibble)
+{
+       P0 = (P0 & 0xf0) | (nibble & 0x0f);
+       P1_1 = rs;
+       P1_0 = 1;
+       ao_lcd_port_delay();
+       P1_0 = 0;
+       ao_lcd_port_delay();
+}
+
+void
+ao_lcd_port_init(void)
+{
+       /* LCD_E and LCD_RS are GPIO outputs */
+       P1DIR |= 0x03;
+       P1SEL &= ~0x03;
+
+       /* LCD D4-D7 are GPIO outputs */
+       P0DIR |= 0x0f;
+       P0SEL &= ~0x0f;
+}
diff --git a/src/cc1111/ao_led.c b/src/cc1111/ao_led.c
new file mode 100644 (file)
index 0000000..5beed58
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+__pdata uint8_t ao_led_enable;
+
+void
+ao_led_on(uint8_t colors)
+{
+       P1 |= (colors & ao_led_enable);
+}
+
+void
+ao_led_off(uint8_t colors)
+{
+       P1 &= ~(colors & ao_led_enable);
+}
+
+void
+ao_led_set(uint8_t colors)
+{
+       P1 = (P1 & ~(ao_led_enable)) | (colors & ao_led_enable);
+}
+
+void
+ao_led_toggle(uint8_t colors)
+{
+       P1 ^= (colors & ao_led_enable);
+}
+
+void
+ao_led_for(uint8_t colors, uint16_t ticks) __reentrant
+{
+       ao_led_on(colors);
+       ao_delay(ticks);
+       ao_led_off(colors);
+}
+
+void
+ao_led_init(uint8_t enable)
+{
+       ao_led_enable = enable;
+       P1SEL &= ~enable;
+       P1 &= ~enable;
+       P1DIR |= enable;
+}
diff --git a/src/cc1111/ao_pins.h b/src/cc1111/ao_pins.h
new file mode 100644 (file)
index 0000000..2f0e288
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_RADIO      1
+
+#if defined(TELEMETRUM_V_1_0)
+       #define HAS_FLIGHT              1
+       #define HAS_USB                 1
+       #define HAS_BEEP                1
+       #define HAS_GPS                 1
+       #define HAS_SERIAL_1            1
+       #define HAS_ADC                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define HAS_DBG                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            1
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+       #define NOISY_ACCEL             1
+
+       #define HAS_COMPANION           1
+       #define COMPANION_CS_ON_P1      1
+       #define AO_COMPANION_CS_PORT    P1
+       #define AO_COMPANION_CS_PIN     2
+       #define AO_COMPANION_CS         P1_2
+
+       #define AO_LED_RED              1
+       #define LEDS_AVAILABLE          (AO_LED_RED)
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL_REF           0
+       #define HAS_ACCEL               1
+       #define HAS_IGNITE              1
+       #define HAS_MONITOR             0
+#endif
+
+#if defined(TELEMETRUM_V_1_1)
+       #define HAS_FLIGHT              1
+       #define HAS_USB                 1
+       #define HAS_BEEP                1
+       #define HAS_GPS                 1
+       #define HAS_SERIAL_1            1
+       #define HAS_ADC                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define HAS_DBG                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            1
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+
+       #define HAS_COMPANION           1
+       #define AO_COMPANION_CS_PORT    P1
+       #define AO_COMPANION_CS_PIN     2
+       #define AO_COMPANION_CS         P1_2
+
+       #define AO_LED_RED              1
+       #define LEDS_AVAILABLE          (AO_LED_RED)
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL_REF           1
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define AO_M25_SPI_CS_MASK      0x02    /* CS0 is P1_1 */
+       #define M25_MAX_CHIPS           1
+       #define HAS_ACCEL               1
+       #define HAS_IGNITE              1
+       #define HAS_MONITOR             0
+#endif
+
+#if defined(TELEMETRUM_V_1_2)
+       #define HAS_FLIGHT              1
+       #define HAS_USB                 1
+       #define HAS_BEEP                1
+       #define HAS_GPS                 1
+       #define HAS_SERIAL_1            1
+       #define HAS_ADC                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define HAS_DBG                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            1
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+
+       #define HAS_COMPANION           1
+       #define AO_COMPANION_CS_PORT    P1
+       #define AO_COMPANION_CS_PIN     2
+       #define AO_COMPANION_CS         P1_2
+
+       #define AO_LED_RED              1
+       #define LEDS_AVAILABLE          (AO_LED_RED)
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL_REF           1
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define AO_M25_SPI_CS_MASK      0x02    /* CS0 is P1_1 */
+       #define M25_MAX_CHIPS           1
+       #define HAS_ACCEL               1
+       #define HAS_IGNITE              1
+       #define HAS_MONITOR             0
+#endif
+
+#if defined(TELEDONGLE_V_0_2)
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                0
+       #define HAS_SERIAL_1            0
+       #define HAS_ADC                 0
+       #define HAS_DBG                 1
+       #define HAS_EEPROM              0
+       #define HAS_LOG                 0
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       1
+       #define PACKET_HAS_SLAVE        0
+       #define AO_LED_RED              1
+       #define AO_LED_GREEN            2
+       #define AO_MONITOR_LED          AO_LED_GREEN
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define HAS_IGNITE              0
+       #define HAS_MONITOR             1
+       #define LEGACY_MONITOR          1
+       #define HAS_RSSI                1
+       #define HAS_AES                 0
+#endif
+
+#if defined(TELEMINI_V_1_0)
+       #define HAS_FLIGHT              1
+       #define HAS_USB                 0
+       #define HAS_BEEP                0
+       #define HAS_GPS                 0
+       #define HAS_SERIAL_1            0
+       #define HAS_ADC                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define HAS_FORCE_FREQ          1
+       #define USE_INTERNAL_FLASH      1
+       #define HAS_DBG                 0
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            1
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+       #define USE_FAST_ASCENT_LOG     1
+
+       #define AO_LED_GREEN            1
+       #define AO_LED_RED              2
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL               0
+       #define HAS_IGNITE              1
+       #define HAS_MONITOR             0
+#endif
+
+#if defined(TELENANO_V_0_1)
+       #define HAS_FLIGHT              1
+       #define HAS_USB                 0
+       #define HAS_BEEP                0
+       #define HAS_GPS                 0
+       #define HAS_SERIAL_1            0
+       #define HAS_ADC                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      1
+       #define HAS_DBG                 0
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            1
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+
+       #define AO_LED_GREEN            1
+       #define AO_LED_RED              2
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL               0
+       #define HAS_IGNITE              0
+       #define HAS_MONITOR             0
+#endif
+
+#if defined(TELEMETRUM_V_0_1)
+       #define HAS_FLIGHT              1
+       #define HAS_USB                 1
+       #define HAS_BEEP                1
+       #define HAS_GPS                 1
+       #define HAS_SERIAL_1            1
+       #define HAS_ADC                 1
+       #define HAS_DBG                 0
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define DBG_ON_P1               0
+       #define DBG_ON_P0               1
+       #define IGNITE_ON_P2            1
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+       #define AO_LED_RED              2
+       #define AO_LED_GREEN            1
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define HAS_EXTERNAL_TEMP       1
+       #define HAS_ACCEL_REF           0
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define HAS_ACCEL               1
+       #define HAS_IGNITE              1
+       #define HAS_MONITOR             0
+       #define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX        ((uint32_t) 127 * (uint32_t) 1024)
+#endif
+
+#if defined(TELEDONGLE_V_0_1)
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                0
+       #define HAS_SERIAL_1            0
+       #define HAS_ADC                 0
+       #define HAS_DBG                 0
+       #define HAS_EEPROM              0
+       #define HAS_LOG                 0
+       #define DBG_ON_P1               0
+       #define DBG_ON_P0               1
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       1
+       #define PACKET_HAS_SLAVE        0
+       #define AO_LED_RED              2
+       #define AO_LED_GREEN            1
+       #define AO_MONITOR_LED          AO_LED_GREEN
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define SPI_CS_ON_P1            0
+       #define SPI_CS_ON_P0            1
+       #define HAS_IGNITE              0
+       #define HAS_MONITOR             1
+       #define LEGACY_MONITOR          1
+       #define HAS_RSSI                1
+       #define HAS_AES                 0
+#endif
+
+#if defined(TIDONGLE)
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                0
+       #define HAS_SERIAL_1            0
+       #define HAS_ADC                 0
+       #define HAS_DBG                 1
+       #define HAS_EEPROM              0
+       #define HAS_LOG                 0
+       #define DBG_ON_P1               0
+       #define DBG_ON_P0               1
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       1
+       #define PACKET_HAS_SLAVE        0
+       #define AO_LED_RED              2
+       #define AO_MONITOR_LED          AO_LED_RED
+       #define LEDS_AVAILABLE          (AO_LED_RED)
+       #define SPI_CS_ON_P1            0
+       #define SPI_CS_ON_P0            1
+       #define HAS_IGNITE              0
+       #define HAS_MONITOR             1
+       #define LEGACY_MONITOR          1
+       #define HAS_RSSI                1
+       #define HAS_AES                 0
+#endif
+
+#if defined(TELEBT_V_0_0)
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                0
+       #define HAS_SERIAL_1            1
+       #define USE_SERIAL_1_STDIN      1
+       #define DELAY_SERIAL_1_STDIN    1
+       #define HAS_ADC                 0
+       #define HAS_DBG                 1
+       #define HAS_EEPROM              0
+       #define HAS_LOG                 0
+       #define HAS_BTM                 1
+       #define DBG_ON_P1               0
+       #define DBG_ON_P0               1
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       1
+       #define PACKET_HAS_SLAVE        0
+       #define AO_LED_RED              2
+       #define AO_LED_GREEN            1
+       #define AO_MONITOR_LED          AO_LED_RED
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define HAS_IGNITE              0
+       #define HAS_IGNITE_REPORT       1
+       #define BT_LINK_ON_P2           1
+       #define BT_LINK_ON_P1           0
+       #define BT_LINK_PIN_INDEX       7
+       #define BT_LINK_PIN             P2_1
+       #define HAS_MONITOR             1
+       #define LEGACY_MONITOR          1
+       #define HAS_RSSI                0
+       #define HAS_AES                 0
+#endif
+
+#if defined(TELEBT_V_0_1)
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                1
+       #define HAS_SERIAL_1            1
+       #define HAS_SERIAL_1_ALT_1      1
+       #define HAS_SERIAL_1_ALT_2      0
+       #define HAS_SERIAL_1_HW_FLOW    1
+       #define USE_SERIAL_1_STDIN      1
+       #define DELAY_SERIAL_1_STDIN    1
+       #define HAS_ADC                 0
+       #define HAS_DBG                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define HAS_BTM                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       1
+       #define PACKET_HAS_SLAVE        0
+       #define AO_LED_RED              1
+       #define AO_LED_GREEN            2
+       #define AO_MONITOR_LED          AO_LED_RED
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define AO_M25_SPI_CS_MASK      0x04    /* CS0 is P1_2 */
+       #define M25_MAX_CHIPS           1
+       #define HAS_ACCEL               0
+       #define HAS_IGNITE              0
+       #define HAS_IGNITE_REPORT       1
+       #define BT_LINK_ON_P2           0
+       #define BT_LINK_ON_P1           1
+       #define BT_LINK_PIN_INDEX       7
+       #define BT_LINK_PIN             P1_7
+       #define HAS_MONITOR             1
+       #define LEGACY_MONITOR          1
+       #define HAS_RSSI                0
+       #define HAS_AES                 0
+#endif
+
+#if defined(TELELAUNCH_V_0_1)
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                1
+       #define HAS_GPS                 0
+       #define HAS_SERIAL_1            1
+       #define HAS_ADC                 1
+       #define HAS_DBG                 0
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 0
+       #define USE_INTERNAL_FLASH      1
+       #define DBG_ON_P1               0
+       #define DBG_ON_P0               1
+       #define IGNITE_ON_P2            1
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        0
+       #define AO_LED_RED              2
+       #define AO_LED_GREEN            1
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define HAS_EXTERNAL_TEMP       1
+       #define HAS_ACCEL_REF           0
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define HAS_ACCEL               0
+       #define HAS_IGNITE              1
+       #define HAS_MONITOR             0
+       #define HAS_AES                 1
+#endif
+
+#if DBG_ON_P1
+
+       #define DBG_CLOCK       (1 << 4)        /* mi0 */
+       #define DBG_DATA        (1 << 5)        /* mo0 */
+       #define DBG_RESET_N     (1 << 3)        /* c0 */
+
+       #define DBG_CLOCK_PIN   (P1_4)
+       #define DBG_DATA_PIN    (P1_5)
+       #define DBG_RESET_N_PIN (P1_3)
+
+       #define DBG_PORT_NUM    1
+       #define DBG_PORT        P1
+       #define DBG_PORT_SEL    P1SEL
+       #define DBG_PORT_INP    P1INP
+       #define DBG_PORT_DIR    P1DIR
+
+#endif /* DBG_ON_P1 */
+
+#if DBG_ON_P0
+
+       #define DBG_CLOCK       (1 << 3)
+       #define DBG_DATA        (1 << 4)
+       #define DBG_RESET_N     (1 << 5)
+
+       #define DBG_CLOCK_PIN   (P0_3)
+       #define DBG_DATA_PIN    (P0_4)
+       #define DBG_RESET_N_PIN (P0_5)
+
+       #define DBG_PORT_NUM    0
+       #define DBG_PORT        P0
+       #define DBG_PORT_SEL    P0SEL
+       #define DBG_PORT_INP    P0INP
+       #define DBG_PORT_DIR    P0DIR
+
+#endif /* DBG_ON_P0 */
+
+#if COMPANION_CS_ON_P1
+       #define COMPANION_CS_PORT       P1
+       #define COMPANION_CS_SEL        P1SEL
+       #define COMPANION_CS_DIR        P1DIR
+#endif
+
+#if SPI_CS_ON_P1
+       #define SPI_CS_PORT     P1
+       #define SPI_CS_SEL      P1SEL
+       #define SPI_CS_DIR      P1DIR
+#endif
+
+#if SPI_CS_ON_P0
+       #define SPI_CS_PORT     P0
+       #define SPI_CS_SEL      P0SEL
+       #define SPI_CS_DIR      P0DIR
+#endif
+
+#define AO_M25_SPI_CS_PORT     SPI_CS_PORT
+
+#ifndef IGNITE_ON_P2
+#error Please define IGNITE_ON_P2
+#endif
+
+#ifndef IGNITE_ON_P0
+#error Please define IGNITE_ON_P0
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#ifndef HAS_EEPROM
+#error Please define HAS_EEPROM
+#endif
+
+#ifndef HAS_LOG
+#error Please define HAS_LOG
+#endif
+
+#if HAS_EEPROM
+#ifndef USE_INTERNAL_FLASH
+#error Please define USE_INTERNAL_FLASH
+#endif
+#endif
+
+#ifndef HAS_DBG
+#error Please define HAS_DBG
+#endif
+
+#ifndef HAS_IGNITE
+#error Please define HAS_IGNITE
+#endif
+
+#if HAS_IGNITE
+#define HAS_IGNITE_REPORT 1
+#endif
+
+#ifndef PACKET_HAS_MASTER
+#error Please define PACKET_HAS_MASTER
+#endif
+
+#ifndef PACKET_HAS_SLAVE
+#error Please define PACKET_HAS_SLAVE
+#endif
+
+#ifndef HAS_MONITOR
+#error Please define HAS_MONITOR
+#endif
+
+#if HAS_MONITOR
+#ifndef HAS_RSSI
+#error Please define HAS_RSSI
+#endif
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#if HAS_ADC
+
+#if HAS_ACCEL
+#ifndef HAS_ACCEL_REF
+#error Please define HAS_ACCEL_REF
+#endif
+#else
+#define HAS_ACCEL_REF 0
+#endif
+
+#endif /* HAS_ADC */
+
+#if IGNITE_ON_P2
+#define AO_IGNITER_PORT                P2
+#define AO_IGNITER_DROGUE_PORT AO_IGNITER_PORT
+#define AO_IGNITER_DROGUE      P2_3
+#define AO_IGNITER_MAIN                P2_4
+#define AO_IGNITER_DIR         P2DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 3)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#define AO_IGNITER_DROGUE_PIN  3
+#define AO_IGNITER_MAIN_PIN    4
+#endif
+
+#if IGNITE_ON_P0
+#define AO_IGNITER_PORT                P0
+#define AO_IGNITER_DROGUE      P0_5
+#define AO_IGNITER_MAIN                P0_4
+#define AO_IGNITER_DIR         P0DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 5)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#define AO_IGNITER_DROGUE_PIN  5
+#define AO_IGNITER_MAIN_PIN    4
+#endif
+
+#define AO_IGNITER_DROGUE_PORT AO_IGNITER_PORT
+#define AO_IGNITER_MAIN_PORT   AO_IGNITER_PORT
+
+/* test these values with real igniters */
+#define AO_IGNITER_OPEN                1000
+#define AO_IGNITER_CLOSED      7000
+#define AO_IGNITER_FIRE_TIME   AO_MS_TO_TICKS(50)
+#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
+
+struct ao_adc {
+       int16_t         accel;          /* accelerometer */
+       int16_t         pres;           /* pressure sensor */
+       int16_t         temp;           /* temperature sensor */
+       int16_t         v_batt;         /* battery voltage */
+       int16_t         sense_d;        /* drogue continuity sense */
+       int16_t         sense_m;        /* main continuity sense */
+#if HAS_ACCEL_REF
+       uint16_t        accel_ref;      /* acceleration reference */
+#endif
+};
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/cc1111/ao_radio.c b/src/cc1111/ao_radio.c
new file mode 100644 (file)
index 0000000..cb2c2fd
--- /dev/null
@@ -0,0 +1,549 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#if HAS_PAD
+#include <ao_pad.h>
+#endif
+
+/* Values from SmartRF® Studio for:
+ *
+ * Deviation:  20.507812 kHz
+ * Datarate:   38.360596 kBaud
+ * Modulation: GFSK
+ * RF Freq:    434.549927 MHz
+ * Channel:    99.975586 kHz
+ * Channel:    0
+ * RX filter:  93.75 kHz
+ */
+
+/*
+ * For IF freq of 140.62kHz, the IF value is:
+ *
+ * 140.62e3 / (24e6 / 2**10) = 6
+ */
+
+#define IF_FREQ_CONTROL        6
+
+/*
+ * For channel bandwidth of 93.75 kHz, the CHANBW_E and CHANBW_M values are
+ *
+ * BW = 24e6 / (8 * (4 + M) * 2 ** E)
+ *
+ * So, M = 0 and E = 3
+ */
+
+#define CHANBW_M       0
+#define CHANBW_E       3
+
+/*
+ * For a symbol rate of 38360kBaud, the DRATE_E and DRATE_M values are:
+ *
+ * R = (256 + M) * 2** E * 24e6 / 2**28
+ *
+ * So M is 163 and E is 10
+ */
+
+#define DRATE_E                10
+#define DRATE_M                163
+
+/*
+ * For a channel deviation of 20.5kHz, the DEVIATION_E and DEVIATION_M values are:
+ *
+ * F = 24e6/2**17 * (8 + DEVIATION_M) * 2**DEVIATION_E
+ *
+ * So M is 6 and E is 3
+ */
+
+#define DEVIATION_M    6
+#define DEVIATION_E    3
+
+/*
+ * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone),
+ * so the DRATE_E and DRATE_M values are:
+ *
+ * M is 94 and E is 6
+ *
+ * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
+ */
+
+#define RDF_DRATE_E    6
+#define RDF_DRATE_M    94
+#define RDF_PACKET_LEN 50
+
+/*
+ * RDF deviation should match the normal NFM value of 5kHz
+ *
+ * M is 6 and E is 1
+ *
+ */
+
+#define RDF_DEVIATION_M        6
+#define RDF_DEVIATION_E        1
+
+/* This are from the table for 433MHz */
+
+#define RF_POWER_M30_DBM       0x12
+#define RF_POWER_M20_DBM       0x0e
+#define RF_POWER_M15_DBM       0x1d
+#define RF_POWER_M10_DBM       0x34
+#define RF_POWER_M5_DBM                0x2c
+#define RF_POWER_0_DBM         0x60
+#define RF_POWER_5_DBM         0x84
+#define RF_POWER_7_DBM         0xc8
+#define RF_POWER_10_DBM                0xc0
+
+#define RF_POWER               RF_POWER_10_DBM
+
+static __code uint8_t radio_setup[] = {
+       RF_PA_TABLE7_OFF,       RF_POWER,
+       RF_PA_TABLE6_OFF,       RF_POWER,
+       RF_PA_TABLE5_OFF,       RF_POWER,
+       RF_PA_TABLE4_OFF,       RF_POWER,
+       RF_PA_TABLE3_OFF,       RF_POWER,
+       RF_PA_TABLE2_OFF,       RF_POWER,
+       RF_PA_TABLE1_OFF,       RF_POWER,
+       RF_PA_TABLE0_OFF,       RF_POWER,
+
+       RF_FSCTRL1_OFF,         (IF_FREQ_CONTROL << RF_FSCTRL1_FREQ_IF_SHIFT),
+       RF_FSCTRL0_OFF,         (0 << RF_FSCTRL0_FREQOFF_SHIFT),
+
+       RF_MDMCFG4_OFF,         ((CHANBW_E << RF_MDMCFG4_CHANBW_E_SHIFT) |
+                                (CHANBW_M << RF_MDMCFG4_CHANBW_M_SHIFT) |
+                                (DRATE_E << RF_MDMCFG4_DRATE_E_SHIFT)),
+       RF_MDMCFG3_OFF,         (DRATE_M << RF_MDMCFG3_DRATE_M_SHIFT),
+       RF_MDMCFG2_OFF,         (RF_MDMCFG2_DEM_DCFILT_OFF |
+                                RF_MDMCFG2_MOD_FORMAT_GFSK |
+                                RF_MDMCFG2_SYNC_MODE_15_16_THRES),
+       RF_MDMCFG1_OFF,         (RF_MDMCFG1_FEC_EN |
+                                RF_MDMCFG1_NUM_PREAMBLE_4 |
+                                (2 << RF_MDMCFG1_CHANSPC_E_SHIFT)),
+       RF_MDMCFG0_OFF,         (17 << RF_MDMCFG0_CHANSPC_M_SHIFT),
+
+       RF_CHANNR_OFF,          0,
+
+       RF_DEVIATN_OFF,         ((DEVIATION_E << RF_DEVIATN_DEVIATION_E_SHIFT) |
+                                (DEVIATION_M << RF_DEVIATN_DEVIATION_M_SHIFT)),
+
+       /* SmartRF says set LODIV_BUF_CURRENT_TX to 0
+        * And, we're not using power ramping, so use PA_POWER 0
+        */
+       RF_FREND0_OFF,          ((1 << RF_FREND0_LODIV_BUF_CURRENT_TX_SHIFT) |
+                                (0 << RF_FREND0_PA_POWER_SHIFT)),
+
+       RF_FREND1_OFF,          ((1 << RF_FREND1_LNA_CURRENT_SHIFT) |
+                                (1 << RF_FREND1_LNA2MIX_CURRENT_SHIFT) |
+                                (1 << RF_FREND1_LODIV_BUF_CURRENT_RX_SHIFT) |
+                                (2 << RF_FREND1_MIX_CURRENT_SHIFT)),
+
+       RF_FSCAL3_OFF,          0xE9,
+       RF_FSCAL2_OFF,          0x0A,
+       RF_FSCAL1_OFF,          0x00,
+       RF_FSCAL0_OFF,          0x1F,
+
+       RF_TEST2_OFF,           0x88,
+       RF_TEST1_OFF,           0x31,
+       RF_TEST0_OFF,           0x09,
+
+       /* default sync values */
+       RF_SYNC1_OFF,           0xD3,
+       RF_SYNC0_OFF,           0x91,
+
+       /* max packet length */
+       RF_PKTCTRL1_OFF,        ((1 << PKTCTRL1_PQT_SHIFT)|
+                                PKTCTRL1_APPEND_STATUS|
+                                PKTCTRL1_ADR_CHK_NONE),
+       RF_PKTCTRL0_OFF,        (RF_PKTCTRL0_WHITE_DATA|
+                                RF_PKTCTRL0_PKT_FORMAT_NORMAL|
+                                RF_PKTCTRL0_CRC_EN|
+                                RF_PKTCTRL0_LENGTH_CONFIG_FIXED),
+       RF_ADDR_OFF,            0x00,
+       RF_MCSM2_OFF,           (RF_MCSM2_RX_TIME_END_OF_PACKET),
+       RF_MCSM1_OFF,           (RF_MCSM1_CCA_MODE_RSSI_BELOW_UNLESS_RECEIVING|
+                                RF_MCSM1_RXOFF_MODE_IDLE|
+                                RF_MCSM1_TXOFF_MODE_IDLE),
+       RF_MCSM0_OFF,           (RF_MCSM0_FS_AUTOCAL_FROM_IDLE|
+                                RF_MCSM0_MAGIC_3|
+                                RF_MCSM0_CLOSE_IN_RX_0DB),
+       RF_FOCCFG_OFF,          (RF_FOCCFG_FOC_PRE_K_3K,
+                                RF_FOCCFG_FOC_POST_K_PRE_K,
+                                RF_FOCCFG_FOC_LIMIT_BW_OVER_4),
+       RF_BSCFG_OFF,           (RF_BSCFG_BS_PRE_K_2K|
+                                RF_BSCFG_BS_PRE_KP_3KP|
+                                RF_BSCFG_BS_POST_KI_PRE_KI|
+                                RF_BSCFG_BS_POST_KP_PRE_KP|
+                                RF_BSCFG_BS_LIMIT_0),
+       RF_AGCCTRL2_OFF,        0x43,
+       RF_AGCCTRL1_OFF,        0x40,
+       RF_AGCCTRL0_OFF,        0x91,
+
+       RF_IOCFG2_OFF,          0x00,
+       RF_IOCFG1_OFF,          0x00,
+       RF_IOCFG0_OFF,          0x00,
+};
+
+static __code uint8_t rdf_setup[] = {
+       RF_MDMCFG4_OFF,         ((CHANBW_E << RF_MDMCFG4_CHANBW_E_SHIFT) |
+                                (CHANBW_M << RF_MDMCFG4_CHANBW_M_SHIFT) |
+                                (RDF_DRATE_E << RF_MDMCFG4_DRATE_E_SHIFT)),
+       RF_MDMCFG3_OFF,         (RDF_DRATE_M << RF_MDMCFG3_DRATE_M_SHIFT),
+       RF_MDMCFG2_OFF,         (RF_MDMCFG2_DEM_DCFILT_OFF |
+                                RF_MDMCFG2_MOD_FORMAT_GFSK |
+                                RF_MDMCFG2_SYNC_MODE_NONE),
+       RF_MDMCFG1_OFF,         (RF_MDMCFG1_FEC_DIS |
+                                RF_MDMCFG1_NUM_PREAMBLE_2 |
+                                (2 << RF_MDMCFG1_CHANSPC_E_SHIFT)),
+
+       RF_DEVIATN_OFF,         ((RDF_DEVIATION_E << RF_DEVIATN_DEVIATION_E_SHIFT) |
+                                (RDF_DEVIATION_M << RF_DEVIATN_DEVIATION_M_SHIFT)),
+
+       /* packet length is set in-line */
+       RF_PKTCTRL1_OFF,        ((1 << PKTCTRL1_PQT_SHIFT)|
+                                PKTCTRL1_ADR_CHK_NONE),
+       RF_PKTCTRL0_OFF,        (RF_PKTCTRL0_PKT_FORMAT_NORMAL|
+                                RF_PKTCTRL0_LENGTH_CONFIG_FIXED),
+};
+
+static __code uint8_t fixed_pkt_setup[] = {
+       RF_MDMCFG4_OFF,         ((CHANBW_E << RF_MDMCFG4_CHANBW_E_SHIFT) |
+                                (CHANBW_M << RF_MDMCFG4_CHANBW_M_SHIFT) |
+                                (DRATE_E << RF_MDMCFG4_DRATE_E_SHIFT)),
+       RF_MDMCFG3_OFF,         (DRATE_M << RF_MDMCFG3_DRATE_M_SHIFT),
+       RF_MDMCFG2_OFF,         (RF_MDMCFG2_DEM_DCFILT_OFF |
+                                RF_MDMCFG2_MOD_FORMAT_GFSK |
+                                RF_MDMCFG2_SYNC_MODE_15_16_THRES),
+       RF_MDMCFG1_OFF,         (RF_MDMCFG1_FEC_EN |
+                                RF_MDMCFG1_NUM_PREAMBLE_4 |
+                                (2 << RF_MDMCFG1_CHANSPC_E_SHIFT)),
+
+       RF_DEVIATN_OFF,         ((DEVIATION_E << RF_DEVIATN_DEVIATION_E_SHIFT) |
+                                (DEVIATION_M << RF_DEVIATN_DEVIATION_M_SHIFT)),
+
+       /* max packet length -- now set inline */
+       RF_PKTCTRL1_OFF,        ((1 << PKTCTRL1_PQT_SHIFT)|
+                                PKTCTRL1_APPEND_STATUS|
+                                PKTCTRL1_ADR_CHK_NONE),
+       RF_PKTCTRL0_OFF,        (RF_PKTCTRL0_WHITE_DATA|
+                                RF_PKTCTRL0_PKT_FORMAT_NORMAL|
+                                RF_PKTCTRL0_CRC_EN|
+                                RF_PKTCTRL0_LENGTH_CONFIG_FIXED),
+};
+
+__xdata uint8_t        ao_radio_dma;
+__xdata uint8_t ao_radio_dma_done;
+__xdata uint8_t ao_radio_done;
+__xdata uint8_t ao_radio_abort;
+__xdata uint8_t ao_radio_mutex;
+
+void
+ao_radio_general_isr(void) __interrupt 16
+{
+       S1CON &= ~0x03;
+       if (RFIF & RFIF_IM_TIMEOUT) {
+               ao_radio_recv_abort();
+               RFIF &= ~ RFIF_IM_TIMEOUT;
+       } else if (RFIF & RFIF_IM_DONE) {
+               ao_radio_done = 1;
+               ao_wakeup(&ao_radio_done);
+               RFIF &= ~RFIF_IM_DONE;
+       }
+}
+
+static void
+ao_radio_set_packet(void)
+{
+       uint8_t i;
+       for (i = 0; i < sizeof (fixed_pkt_setup); i += 2)
+               RF[fixed_pkt_setup[i]] = fixed_pkt_setup[i+1];
+}
+
+static void
+ao_radio_idle(void)
+{
+       if (RF_MARCSTATE != RF_MARCSTATE_IDLE)
+       {
+               do {
+                       RFST = RFST_SIDLE;
+                       ao_yield();
+               } while (RF_MARCSTATE != RF_MARCSTATE_IDLE);
+       }
+}
+
+#define ao_radio_put() ao_mutex_put(&ao_radio_mutex)
+
+static void
+ao_radio_get(uint8_t len)
+{
+       ao_config_get();
+       ao_mutex_get(&ao_radio_mutex);
+       ao_radio_idle();
+       RF_CHANNR = 0;
+       RF_FREQ2 = (uint8_t) (ao_config.radio_setting >> 16);
+       RF_FREQ1 = (uint8_t) (ao_config.radio_setting >> 8);
+       RF_FREQ0 = (uint8_t) (ao_config.radio_setting);
+       RF_PKTLEN = len;
+}
+
+
+void
+ao_radio_send(__xdata void *packet, uint8_t size) __reentrant
+{
+       ao_radio_get(size);
+       ao_radio_done = 0;
+       ao_dma_set_transfer(ao_radio_dma,
+                           packet,
+                           &RFDXADDR,
+                           size,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           DMA_CFG0_TRIGGER_RADIO,
+                           DMA_CFG1_SRCINC_1 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_HIGH);
+       ao_dma_start(ao_radio_dma);
+       RFST = RFST_STX;
+       __critical while (!ao_radio_done)
+               ao_sleep(&ao_radio_done);
+       ao_radio_put();
+}
+
+uint8_t
+ao_radio_recv(__xdata void *packet, uint8_t size) __reentrant
+{
+       ao_radio_abort = 0;
+       ao_radio_get(size - 2);
+       ao_dma_set_transfer(ao_radio_dma,
+                           &RFDXADDR,
+                           packet,
+                           size,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           DMA_CFG0_TRIGGER_RADIO,
+                           DMA_CFG1_SRCINC_0 |
+                           DMA_CFG1_DESTINC_1 |
+                           DMA_CFG1_PRIORITY_HIGH);
+       ao_dma_start(ao_radio_dma);
+       RFST = RFST_SRX;
+
+       /* Wait for DMA to be done, for the radio receive process to
+        * get aborted or for a receive timeout to fire
+        */
+       __critical while (!ao_radio_dma_done && !ao_radio_abort)
+                          if (ao_sleep(&ao_radio_dma_done))
+                                  break;
+
+       /* If recv was aborted, clean up by stopping the DMA engine
+        * and idling the radio
+        */
+       if (!ao_radio_dma_done) {
+               ao_dma_abort(ao_radio_dma);
+               ao_radio_idle();
+       }
+       ao_radio_put();
+       return ao_radio_dma_done;
+}
+
+/*
+ * Wake up a task waiting to receive a radio packet
+ * and tell them to abort the transfer
+ */
+
+void
+ao_radio_recv_abort(void)
+{
+       ao_radio_abort = 1;
+       ao_wakeup(&ao_radio_dma_done);
+}
+
+__code ao_radio_rdf_value = 0x55;
+
+static void
+ao_radio_rdf_start(void)
+{
+       uint8_t i;
+       ao_radio_abort = 0;
+       ao_radio_get(AO_RADIO_RDF_LEN);
+       ao_radio_done = 0;
+       for (i = 0; i < sizeof (rdf_setup); i += 2)
+               RF[rdf_setup[i]] = rdf_setup[i+1];
+}
+
+static void
+ao_radio_rdf_run(void)
+{
+       ao_dma_start(ao_radio_dma);
+       RFST = RFST_STX;
+       __critical while (!ao_radio_done && !ao_radio_abort)
+                          ao_sleep(&ao_radio_done);
+       if (!ao_radio_done) {
+               ao_dma_abort(ao_radio_dma);
+               ao_radio_idle();
+       }
+       ao_radio_set_packet();
+       ao_radio_put();
+}
+
+void
+ao_radio_rdf(void)
+{
+       ao_radio_rdf_start();
+
+       ao_dma_set_transfer(ao_radio_dma,
+                           CODE_TO_XDATA(&ao_radio_rdf_value),
+                           &RFDXADDR,
+                           AO_RADIO_RDF_LEN,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           DMA_CFG0_TRIGGER_RADIO,
+                           DMA_CFG1_SRCINC_0 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_HIGH);
+       ao_radio_rdf_run();
+}
+
+#define PA     0x00
+#define BE     0x55
+
+#define CONT_PAUSE_8   PA, PA, PA, PA, PA, PA, PA, PA
+#define CONT_PAUSE_16  CONT_PAUSE_8, CONT_PAUSE_8
+#define CONT_PAUSE_24  CONT_PAUSE_16, CONT_PAUSE_8
+
+#define CONT_BEEP_8    BE, BE, BE, BE, BE, BE, BE, BE
+
+#if AO_RADIO_CONT_PAUSE_LEN == 24
+#define CONT_PAUSE     CONT_PAUSE_24
+#endif
+
+#if AO_RADIO_CONT_TONE_LEN == 8
+#define CONT_BEEP              CONT_BEEP_8
+#define CONT_PAUSE_SHORT       CONT_PAUSE_8
+#endif
+
+#define CONT_ADDR(c)   CODE_TO_XDATA(&ao_radio_cont[(3-(c)) * (AO_RADIO_CONT_PAUSE_LEN + AO_RADIO_CONT_TONE_LEN)])
+
+__code uint8_t ao_radio_cont[] = {
+       CONT_PAUSE, CONT_BEEP,
+       CONT_PAUSE, CONT_BEEP,
+       CONT_PAUSE, CONT_BEEP,
+       CONT_PAUSE, CONT_PAUSE_SHORT,
+       CONT_PAUSE, CONT_PAUSE_SHORT,
+       CONT_PAUSE,
+};
+
+void
+ao_radio_continuity(uint8_t c)
+{
+       ao_radio_rdf_start();
+       ao_dma_set_transfer(ao_radio_dma,
+                           CONT_ADDR(c),
+                           &RFDXADDR,
+                           AO_RADIO_CONT_TOTAL_LEN,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           DMA_CFG0_TRIGGER_RADIO,
+                           DMA_CFG1_SRCINC_1 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_HIGH);
+       ao_radio_rdf_run();
+}
+
+void
+ao_radio_rdf_abort(void)
+{
+       ao_radio_abort = 1;
+       ao_wakeup(&ao_radio_done);
+}
+
+
+/* Output carrier */
+
+static __xdata ao_radio_test_on;
+
+void
+ao_radio_test(uint8_t on)
+{
+       if (on) {
+               if (!ao_radio_test_on) {
+#if HAS_MONITOR
+                       ao_monitor_disable();
+#endif
+#if PACKET_HAS_SLAVE
+                       ao_packet_slave_stop();
+#endif
+#if HAS_PAD
+                       ao_pad_disable();
+#endif
+                       ao_radio_get(0xff);
+                       RFST = RFST_STX;
+                       ao_radio_test_on = 1;
+               }
+       } else  {
+               if (ao_radio_test_on) {
+                       ao_radio_idle();
+                       ao_radio_put();
+                       ao_radio_test_on = 0;
+#if HAS_MONITOR
+                       ao_monitor_enable();
+#endif
+#if HAS_PAD
+                       ao_pad_enable();
+#endif
+               }
+       }
+}
+
+static void
+ao_radio_test_cmd(void)
+{
+       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))
+               ao_radio_test(1);
+       if (mode == 3) {
+               printf ("Hit a character to stop..."); flush();
+               getchar();
+               putchar('\n');
+       }
+       if ((mode & 1))
+               ao_radio_test(0);
+}
+
+__code struct ao_cmds ao_radio_cmds[] = {
+       { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
+       { 0,    NULL },
+};
+
+void
+ao_radio_init(void)
+{
+       uint8_t i;
+       for (i = 0; i < sizeof (radio_setup); i += 2)
+               RF[radio_setup[i]] = radio_setup[i+1];
+       ao_radio_set_packet();
+       ao_radio_dma_done = 1;
+       ao_radio_dma = ao_dma_alloc(&ao_radio_dma_done);
+       RFIF = 0;
+       RFIM = RFIM_IM_TIMEOUT|RFIM_IM_DONE;
+       IEN2 |= IEN2_RFIE;
+       ao_cmd_register(&ao_radio_cmds[0]);
+}
diff --git a/src/cc1111/ao_reboot.c b/src/cc1111/ao_reboot.c
new file mode 100644 (file)
index 0000000..8c47b89
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/* Use the watchdog timer to force a complete reboot
+ */
+void
+ao_reboot(void)
+{
+       WDCTL = WDCTL_EN | WDCTL_MODE_WATCHDOG | WDCTL_INT_32768;
+       ao_delay(AO_SEC_TO_TICKS(2));
+       ao_panic(AO_PANIC_REBOOT);
+}
diff --git a/src/cc1111/ao_romconfig.c b/src/cc1111/ao_romconfig.c
new file mode 100644 (file)
index 0000000..f3fe61b
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#include "ao.h"
+
+__code __at (0x00a0) uint16_t ao_romconfig_version = AO_ROMCONFIG_VERSION;
+__code __at (0x00a2) uint16_t ao_romconfig_check = ~AO_ROMCONFIG_VERSION;
+__code __at (0x00a4) uint16_t ao_serial_number = 0;
+/*
+ * For 434.550MHz, the frequency value is:
+ *
+ * 434.550e6 / (24e6 / 2**16) = 1186611.2
+ *
+ * This value is stored in a const variable so that
+ * ao-load can change it during programming for
+ * devices that have no eeprom for config data.
+ */
+__code __at (0x00a6) uint32_t ao_radio_cal = 1186611;
diff --git a/src/cc1111/ao_serial.c b/src/cc1111/ao_serial.c
new file mode 100644 (file)
index 0000000..4838380
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+const __code struct ao_serial_speed ao_serial_speeds[] = {
+       /* [AO_SERIAL_SPEED_4800] = */ {
+               /* .baud = */ 163,
+               /* .gcr  = */ (7 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
+       },
+       /* [AO_SERIAL_SPEED_9600] = */ {
+               /* .baud = */ 163,
+               /* .gcr  = */ (8 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
+       },
+       /* [AO_SERIAL_SPEED_19200] = */ {
+               /* .baud = */ 163,
+               /* .gcr  = */ (9 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
+       },
+       /* [AO_SERIAL_SPEED_57600] = */ {
+               /* .baud = */ 59,
+               /* .gcr =  */ (11 << UxGCR_BAUD_E_SHIFT) | UxGCR_ORDER_LSB
+       },
+};
+
+#if HAS_SERIAL_0
+
+volatile __xdata struct ao_fifo        ao_serial0_rx_fifo;
+volatile __xdata struct ao_fifo        ao_serial0_tx_fifo;
+
+void
+ao_serial0_rx_isr(void) __interrupt 2
+{
+       if (!ao_fifo_full(ao_serial0_rx_fifo))
+               ao_fifo_insert(ao_serial0_rx_fifo, U0DBUF);
+       ao_wakeup(&ao_serial0_rx_fifo);
+#if USE_SERIAL_0_STDIN
+       ao_wakeup(&ao_stdin_ready);
+#endif
+}
+
+static __xdata uint8_t ao_serial0_tx_started;
+
+static void
+ao_serial0_tx_start(void)
+{
+       if (!ao_fifo_empty(ao_serial0_tx_fifo) &&
+           !ao_serial0_tx_started)
+       {
+               ao_serial0_tx_started = 1;
+               ao_fifo_remove(ao_serial0_tx_fifo, U0DBUF);
+       }
+}
+
+void
+ao_serial0_tx_isr(void) __interrupt 7
+{
+       UTX0IF = 0;
+       ao_serial0_tx_started = 0;
+       ao_serial0_tx_start();
+       ao_wakeup(&ao_serial0_tx_fifo);
+}
+
+char
+ao_serial0_getchar(void) __critical
+{
+       char    c;
+       while (ao_fifo_empty(ao_serial0_rx_fifo))
+               ao_sleep(&ao_serial0_rx_fifo);
+       ao_fifo_remove(ao_serial0_rx_fifo, c);
+       return c;
+}
+
+#if USE_SERIAL_0_STDIN
+char
+ao_serial0_pollchar(void) __critical
+{
+       char    c;
+       if (ao_fifo_empty(ao_serial0_rx_fifo))
+               return AO_READ_AGAIN;
+       ao_fifo_remove(ao_serial0_rx_fifo,c);
+       return c;
+}
+#endif
+
+void
+ao_serial0_putchar(char c) __critical
+{
+       while (ao_fifo_full(ao_serial0_tx_fifo))
+               ao_sleep(&ao_serial0_tx_fifo);
+       ao_fifo_insert(ao_serial0_tx_fifo, c);
+       ao_serial0_tx_start();
+}
+
+void
+ao_serial0_drain(void) __critical
+{
+       while (!ao_fifo_empty(ao_serial0_tx_fifo))
+               ao_sleep(&ao_serial0_tx_fifo);
+}
+
+void
+ao_serial0_set_speed(uint8_t speed)
+{
+       ao_serial0_drain();
+       if (speed > AO_SERIAL_SPEED_57600)
+               return;
+       U0UCR |= UxUCR_FLUSH;
+       U0BAUD = ao_serial_speeds[speed].baud;
+       U0GCR = ao_serial_speeds[speed].gcr;
+}
+#endif /* HAS_SERIAL_0 */
+
+#if HAS_SERIAL_1
+
+volatile __xdata struct ao_fifo        ao_serial1_rx_fifo;
+volatile __xdata struct ao_fifo        ao_serial1_tx_fifo;
+
+void
+ao_serial1_rx_isr(void) __interrupt 3
+{
+       if (!ao_fifo_full(ao_serial1_rx_fifo))
+               ao_fifo_insert(ao_serial1_rx_fifo, U1DBUF);
+       ao_wakeup(&ao_serial1_rx_fifo);
+#if USE_SERIAL_1_STDIN
+       ao_wakeup(&ao_stdin_ready);
+#endif
+}
+
+static __xdata uint8_t ao_serial1_tx_started;
+
+static void
+ao_serial1_tx_start(void)
+{
+       if (!ao_fifo_empty(ao_serial1_tx_fifo) &&
+           !ao_serial1_tx_started)
+       {
+               ao_serial1_tx_started = 1;
+               ao_fifo_remove(ao_serial1_tx_fifo, U1DBUF);
+       }
+}
+
+void
+ao_serial1_tx_isr(void) __interrupt 14
+{
+       UTX1IF = 0;
+       ao_serial1_tx_started = 0;
+       ao_serial1_tx_start();
+       ao_wakeup(&ao_serial1_tx_fifo);
+}
+
+char
+ao_serial1_getchar(void) __critical
+{
+       char    c;
+       while (ao_fifo_empty(ao_serial1_rx_fifo))
+               ao_sleep(&ao_serial1_rx_fifo);
+       ao_fifo_remove(ao_serial1_rx_fifo, c);
+       return c;
+}
+
+#if USE_SERIAL_1_STDIN
+char
+ao_serial1_pollchar(void) __critical
+{
+       char    c;
+       if (ao_fifo_empty(ao_serial1_rx_fifo))
+               return AO_READ_AGAIN;
+       ao_fifo_remove(ao_serial1_rx_fifo,c);
+       return c;
+}
+#endif
+
+void
+ao_serial1_putchar(char c) __critical
+{
+       while (ao_fifo_full(ao_serial1_tx_fifo))
+               ao_sleep(&ao_serial1_tx_fifo);
+       ao_fifo_insert(ao_serial1_tx_fifo, c);
+       ao_serial1_tx_start();
+}
+
+void
+ao_serial1_drain(void) __critical
+{
+       while (!ao_fifo_empty(ao_serial1_tx_fifo))
+               ao_sleep(&ao_serial1_tx_fifo);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+       ao_serial1_drain();
+       if (speed > AO_SERIAL_SPEED_57600)
+               return;
+       U1UCR |= UxUCR_FLUSH;
+       U1BAUD = ao_serial_speeds[speed].baud;
+       U1GCR = ao_serial_speeds[speed].gcr;
+}
+
+#endif /* HAS_SERIAL_1 */
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_0
+#if HAS_SERIAL_0_ALT_1
+       /* Set up the USART pin assignment */
+       PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_1;
+
+       P2DIR = (P2DIR & ~P2DIR_PRIP0_MASK) | P2DIR_PRIP0_USART0_USART1;
+
+       /* Make the USART pins be controlled by the USART */
+       P0SEL |= (1 << 2) | (1 << 3);
+#if HAS_SERIAL_0_HW_FLOW
+       P0SEL |= (1 << 4) | (1 << 5);
+#endif
+#else
+       /* Set up the USART pin assignment */
+       PERCFG = (PERCFG & ~PERCFG_U0CFG_ALT_MASK) | PERCFG_U0CFG_ALT_2;
+
+       P2SEL = (P2SEL & ~(P2SEL_PRI3P1_MASK | P2SEL_PRI0P1_MASK)) |
+               (P2SEL_PRI3P1_USART0 | P2SEL_PRI0P1_USART0);
+
+       /* Make the USART pins be controlled by the USART */
+       P1SEL |= (1 << 5) | (1 << 4);
+#if HAS_SERIAL_0_HW_FLOW
+       P1SEL |= (1 << 3) | (1 << 2);
+#endif
+#endif
+
+       /* UART mode with receiver enabled */
+       U0CSR = (UxCSR_MODE_UART | UxCSR_RE);
+
+       /* Pick a 9600 baud rate */
+       ao_serial0_set_speed(AO_SERIAL_SPEED_9600);
+
+       /* Reasonable serial parameters */
+       U0UCR = (UxUCR_FLUSH |
+#if HAS_SERIAL_0_HW_FLOW
+                UxUCR_FLOW_ENABLE |
+#else
+                UxUCR_FLOW_DISABLE |
+#endif
+                UxUCR_D9_EVEN_PARITY |
+                UxUCR_BIT9_8_BITS |
+                UxUCR_PARITY_DISABLE |
+                UxUCR_SPB_1_STOP_BIT |
+                UxUCR_STOP_HIGH |
+                UxUCR_START_LOW);
+
+       IEN0 |= IEN0_URX0IE;
+       IEN2 |= IEN2_UTX0IE;
+#if USE_SERIAL_0_STDIN && !DELAY_SERIAL_0_STDIN
+       ao_add_stdio(ao_serial0_pollchar,
+                    ao_serial0_putchar,
+                    NULL);
+#endif
+#endif /* HAS_SERIAL_0 */
+
+#if HAS_SERIAL_1
+#if HAS_SERIAL_1_ALT_1
+       /* Set up the USART pin assignment */
+       PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_1;
+
+       P2DIR = (P2DIR & ~P2DIR_PRIP0_MASK) | P2DIR_PRIP0_USART1_USART0;
+
+       /* Make the USART pins be controlled by the USART */
+       P0SEL |= (1 << 5) | (1 << 4);
+#if HAS_SERIAL_1_HW_FLOW
+       P0SEL |= (1 << 3) | (1 << 2);
+#endif
+#else
+       /* Set up the USART pin assignment */
+       PERCFG = (PERCFG & ~PERCFG_U1CFG_ALT_MASK) | PERCFG_U1CFG_ALT_2;
+
+       P2SEL = (P2SEL & ~(P2SEL_PRI3P1_MASK | P2SEL_PRI2P1_MASK)) |
+               (P2SEL_PRI3P1_USART1 | P2SEL_PRI2P1_USART1);
+
+       /* Make the USART pins be controlled by the USART */
+       P1SEL |= (1 << 6) | (1 << 7);
+#if HAS_SERIAL_1_HW_FLOW
+       P1SEL |= (1 << 5) | (1 << 4);
+#endif
+#endif
+
+       /* UART mode with receiver enabled */
+       U1CSR = (UxCSR_MODE_UART | UxCSR_RE);
+
+       /* Pick a 4800 baud rate */
+       ao_serial1_set_speed(AO_SERIAL_SPEED_4800);
+
+       /* Reasonable serial parameters */
+       U1UCR = (UxUCR_FLUSH |
+#if HAS_SERIAL_1_HW_FLOW
+                UxUCR_FLOW_ENABLE |
+#else
+                UxUCR_FLOW_DISABLE |
+#endif
+                UxUCR_D9_EVEN_PARITY |
+                UxUCR_BIT9_8_BITS |
+                UxUCR_PARITY_DISABLE |
+                UxUCR_SPB_1_STOP_BIT |
+                UxUCR_STOP_HIGH |
+                UxUCR_START_LOW);
+
+       IEN0 |= IEN0_URX1IE;
+       IEN2 |= IEN2_UTX1IE;
+
+#if USE_SERIAL_1_STDIN && !DELAY_SERIAL_1_STDIN
+       ao_add_stdio(ao_serial1_pollchar,
+                    ao_serial1_putchar,
+                    NULL);
+#endif
+#endif /* HAS_SERIAL_1 */
+}
diff --git a/src/cc1111/ao_spi.c b/src/cc1111/ao_spi.c
new file mode 100644 (file)
index 0000000..cdef6bd
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+#include "ao.h"
+
+/* Default pin usage for existing Altus Metrum devices */
+#if !HAS_SPI_0 && !HAS_SPI_1
+#define HAS_SPI_0      1
+#define SPI_0_ALT_2    1
+#endif
+
+#ifndef SPI_CONST
+#define SPI_CONST      0xff
+#endif
+
+/*
+ * USART0 SPI config alt 1
+ * 
+ *     MO      P0_3
+ *     MI      P0_2
+ *     CLK     P0_5
+ *     SS      P0_4
+ *
+ * USART0 SPI config alt 2
+ *
+ *     MO      P1_5
+ *     MI      P1_4
+ *     CLK     P1_3
+ *     CSS     P1_2
+ *
+ * USART1 SPI config alt 1
+ *
+ *     MO      P0_4
+ *     MI      P0_5
+ *     CLK     P0_3
+ *     SS      P0_2
+ *
+ * USART1 SPI config alt 2
+ *
+ *     MO      P1_6
+ *     MI      P1_7
+ *     CLK     P1_5
+ *     SS      P1_4
+ *
+ *
+ * Chip select is the responsibility of the caller in master mode
+ */
+
+#if HAS_SPI_0
+#define SPI_CSR                U0CSR
+#define SPI_BUF                U0DBUFXADDR
+#define SPI_BAUD       U0BAUD
+#define SPI_GCR                U0GCR
+#define SPI_CFG_MASK   PERCFG_U0CFG_ALT_MASK
+#define SPI_DMA_TX     DMA_CFG0_TRIGGER_UTX0
+#define SPI_DMA_RX     DMA_CFG0_TRIGGER_URX0
+
+#if SPI_0_ALT_1
+#define SPI_CFG                PERCFG_U0CFG_ALT_1
+#define SPI_SEL                P0SEL
+#define SPI_BITS       (1 << 3) | (1 << 2) | (1 << 5)
+#define SPI_CSS_BIT    (1 << 4)
+#endif
+
+#if SPI_0_ALT_2
+#define SPI_CFG                PERCFG_U0CFG_ALT_2
+#define SPI_SEL                P1SEL
+#define SPI_PRI                P2SEL_PRI3P1_USART0
+#define SPI_BITS       (1 << 5) | (1 << 4) | (1 << 3)
+#define SPI_CSS_BIT    (1 << 2)
+#endif
+
+#endif
+
+#if HAS_SPI_1
+#define SPI_CSR                U1CSR
+#define SPI_BUF                U1DBUFXADDR
+#define SPI_BAUD       U1BAUD
+#define SPI_GCR                U1GCR
+#define SPI_CFG_MASK   PERCFG_U1CFG_ALT_MASK
+#define SPI_DMA_TX     DMA_CFG0_TRIGGER_UTX1
+#define SPI_DMA_RX     DMA_CFG0_TRIGGER_URX1
+
+#if SPI_1_ALT_1
+#define SPI_CFG                PERCFG_U1CFG_ALT_1
+#define SPI_SEL                P0SEL
+#define SPI_BITS       (1 << 4) | (1 << 5) | (1 << 3)
+#define SPI_CSS_BIT    (1 << 2)
+#endif
+
+#if SPI_1_ALT_2
+#define SPI_CFG                PERCFG_U1CFG_ALT_2
+#define SPI_SEL                P1SEL
+#define SPI_PRI                P2SEL_PRI3P1_USART1
+#define SPI_BITS       (1 << 6) | (1 << 7) | (1 << 5)
+#define SPI_CSS_BIT    (1 << 4)
+#endif
+
+#endif
+
+#if AO_SPI_SLAVE
+#define CSS            SPI_CSS_BIT
+#define UxCSR_DIRECTION        UxCSR_SLAVE
+#else
+#define CSS            0
+#define UxCSR_DIRECTION        UxCSR_MASTER
+#endif
+
+/* Shared mutex to protect SPI bus, must cover the entire
+ * operation, from CS low to CS high. This means that any SPI
+ * user must protect the SPI bus with this mutex
+ */
+__xdata uint8_t        ao_spi_mutex;
+__xdata uint8_t ao_spi_dma_in_done;
+__xdata uint8_t ao_spi_dma_out_done;
+
+uint8_t        ao_spi_dma_out_id;
+uint8_t ao_spi_dma_in_id;
+
+static __xdata uint8_t ao_spi_const;
+
+/* Send bytes over SPI.
+ *
+ * This sets up two DMA engines, one writing the data and another reading
+ * bytes coming back.  We use the bytes coming back to tell when the transfer
+ * is complete, as the transmit register is double buffered and hence signals
+ * completion one byte before the transfer is actually complete
+ */
+void
+ao_spi_send_bus(void __xdata *block, uint16_t len) __reentrant
+{
+       ao_dma_set_transfer(ao_spi_dma_in_id,
+                           &SPI_BUF,
+                           &ao_spi_const,
+                           len,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           SPI_DMA_RX,
+                           DMA_CFG1_SRCINC_0 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_NORMAL);
+       ao_dma_set_transfer(ao_spi_dma_out_id,
+                           block,
+                           &SPI_BUF,
+                           len,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           SPI_DMA_TX,
+                           DMA_CFG1_SRCINC_1 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_NORMAL);
+
+       ao_dma_start(ao_spi_dma_in_id);
+       ao_dma_start(ao_spi_dma_out_id);
+       ao_dma_trigger(ao_spi_dma_out_id);
+#if !AO_SPI_SLAVE
+       __critical while (!ao_spi_dma_in_done)
+               ao_sleep(&ao_spi_dma_in_done);
+#endif
+}
+
+#if AO_SPI_SLAVE
+void
+ao_spi_send_wait(void)
+{
+       __critical while (!ao_spi_dma_in_done)
+               ao_sleep(&ao_spi_dma_in_done);
+}
+#endif
+
+/* Receive bytes over SPI.
+ *
+ * This sets up tow DMA engines, one reading the data and another
+ * writing constant values to the SPI transmitter as that is what
+ * clocks the data coming in.
+ */
+void
+ao_spi_recv_bus(void __xdata *block, uint16_t len) __reentrant
+{
+       ao_dma_set_transfer(ao_spi_dma_in_id,
+                           &SPI_BUF,
+                           block,
+                           len,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           SPI_DMA_RX,
+                           DMA_CFG1_SRCINC_0 |
+                           DMA_CFG1_DESTINC_1 |
+                           DMA_CFG1_PRIORITY_NORMAL);
+
+       ao_spi_const = SPI_CONST;
+
+#if !AO_SPI_SLAVE
+       ao_dma_set_transfer(ao_spi_dma_out_id,
+                           &ao_spi_const,
+                           &SPI_BUF,
+                           len,
+                           DMA_CFG0_WORDSIZE_8 |
+                           DMA_CFG0_TMODE_SINGLE |
+                           SPI_DMA_TX,
+                           DMA_CFG1_SRCINC_0 |
+                           DMA_CFG1_DESTINC_0 |
+                           DMA_CFG1_PRIORITY_NORMAL);
+#endif
+
+       ao_dma_start(ao_spi_dma_in_id);
+#if !AO_SPI_SLAVE
+       ao_dma_start(ao_spi_dma_out_id);
+       ao_dma_trigger(ao_spi_dma_out_id);
+       __critical while (!ao_spi_dma_in_done)
+               ao_sleep(&ao_spi_dma_in_done);
+#endif
+}
+
+#if AO_SPI_SLAVE
+void
+ao_spi_recv_wait(void)
+{
+       __critical while (!ao_spi_dma_in_done)
+               ao_sleep(&ao_spi_dma_in_done);
+}
+#endif
+
+void
+ao_spi_init(void)
+{
+       /* Set up the USART pin assignment */
+       PERCFG = (PERCFG & ~SPI_CFG_MASK) | SPI_CFG;
+
+       /* Ensure that SPI USART takes precidence over the other USART
+        * for pins that they share
+        */
+#ifdef SPI_PRI
+       P2SEL = (P2SEL & ~P2SEL_PRI3P1_MASK) | SPI_PRI;
+#endif
+
+       /* Make the SPI pins be controlled by the USART peripheral */
+       SPI_SEL |= SPI_BITS | CSS;
+
+       /* Set up OUT DMA */
+       ao_spi_dma_out_id = ao_dma_alloc(&ao_spi_dma_out_done);
+
+       /* Set up IN DMA */
+       ao_spi_dma_in_id = ao_dma_alloc(&ao_spi_dma_in_done);
+
+       /* Set up the USART.
+        *
+        * SPI master/slave mode
+        */
+       SPI_CSR = (UxCSR_MODE_SPI | UxCSR_RE | UxCSR_DIRECTION);
+
+       /* Set the baud rate and signal parameters
+        *
+        * The cc1111 is limited to a 24/8 MHz SPI clock.
+        * Every peripheral I've ever seen goes faster than that,
+        * so set the clock to 3MHz (BAUD_E 17, BAUD_M 0)
+        */
+       SPI_BAUD = 0;
+       SPI_GCR = (UxGCR_CPOL_NEGATIVE |
+                  UxGCR_CPHA_FIRST_EDGE |
+                  UxGCR_ORDER_MSB |
+                  (17 << UxGCR_BAUD_E_SHIFT));
+}
diff --git a/src/cc1111/ao_string.c b/src/cc1111/ao_string.c
new file mode 100644 (file)
index 0000000..3a07e47
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+_ao_xmemcpy(__xdata void *dst, __xdata void *src, uint8_t count)
+{
+       while (count--) {
+               *(__xdata uint8_t *) dst = *(__xdata uint8_t *) src;
+               dst = (__xdata uint8_t *) dst + 1;
+               src = (__xdata uint8_t *) src + 1;
+       }
+}
+
+void
+_ao_xmemset(__xdata void *dst, uint8_t v, uint8_t count)
+{
+       while (count--) {
+               *(__xdata uint8_t *) dst = v;
+               dst = (__xdata uint8_t *) dst + 1;
+       }
+}
+
+int8_t
+_ao_xmemcmp(__xdata void *a, __xdata void *b, uint8_t count)
+{
+       while (count--) {
+               int8_t  d = *(__xdata int8_t *) a - *(__xdata int8_t *) b;
+               if (d)
+                       return d;
+               a = (__xdata int8_t *) a + 1;
+               b = (__xdata int8_t *) b + 1;
+       }
+       return 0;
+}
diff --git a/src/cc1111/ao_timer.c b/src/cc1111/ao_timer.c
new file mode 100644 (file)
index 0000000..a64b5ab
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+volatile __data uint16_t ao_tick_count;
+
+uint16_t ao_time(void) __critical
+{
+       return ao_tick_count;
+}
+
+#define T1_CLOCK_DIVISOR       8       /* 24e6/8 = 3e6 */
+#define T1_SAMPLE_TIME         30000   /* 3e6/30000 = 100 */
+
+#if HAS_ADC
+volatile __data uint8_t        ao_adc_interval = 1;
+volatile __data uint8_t        ao_adc_count;
+#endif
+
+void ao_timer_isr(void) __interrupt 9
+{
+       ++ao_tick_count;
+#if HAS_ADC
+       if (++ao_adc_count == ao_adc_interval) {
+               ao_adc_count = 0;
+               ao_adc_poll();
+       }
+#endif
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval) __critical
+{
+       ao_adc_interval = interval;
+       ao_adc_count = 0;
+}
+#endif
+
+void
+ao_timer_init(void)
+{
+       /* NOTE:  This uses a timer only present on cc1111 architecture. */
+
+       /* disable timer 1 */
+       T1CTL = 0;
+
+       /* set the sample rate */
+       T1CC0H = T1_SAMPLE_TIME >> 8;
+       T1CC0L = (uint8_t) T1_SAMPLE_TIME;
+
+       T1CCTL0 = T1CCTL_MODE_COMPARE;
+       T1CCTL1 = 0;
+       T1CCTL2 = 0;
+
+       /* clear timer value */
+       T1CNTL = 0;
+
+       /* enable overflow interrupt */
+       OVFIM = 1;
+       /* enable timer 1 interrupt */
+       T1IE = 1;
+
+       /* enable timer 1 in module mode, dividing by 8 */
+       T1CTL = T1CTL_MODE_MODULO | T1CTL_DIV_8;
+}
+
+/*
+ * AltOS always cranks the clock to the max frequency
+ */
+void
+ao_clock_init(void)
+{
+       /* Switch system clock to crystal oscilator */
+       CLKCON = (CLKCON & ~CLKCON_OSC_MASK) | (CLKCON_OSC_XTAL);
+
+       while (!(SLEEP & SLEEP_XOSC_STB))
+               ;
+
+       /* Crank up the timer tick and system clock speed */
+       CLKCON = ((CLKCON & ~(CLKCON_TICKSPD_MASK | CLKCON_CLKSPD_MASK)) |
+                 (CLKCON_TICKSPD_1 | CLKCON_CLKSPD_1));
+
+       while ((CLKCON & (CLKCON_TICKSPD_MASK|CLKCON_CLKSPD_MASK)) !=
+              (CLKCON_TICKSPD_1 | CLKCON_CLKSPD_1))
+               ;
+}
diff --git a/src/cc1111/ao_usb.c b/src/cc1111/ao_usb.c
new file mode 100644 (file)
index 0000000..ce26e80
--- /dev/null
@@ -0,0 +1,463 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_usb.h"
+
+struct ao_task __xdata ao_usb_task;
+
+static __xdata uint16_t        ao_usb_in_bytes;
+static __pdata uint16_t ao_usb_in_bytes_last;
+static __xdata uint16_t        ao_usb_out_bytes;
+static __pdata uint8_t ao_usb_iif;
+static __pdata uint8_t ao_usb_running;
+
+static void
+ao_usb_set_interrupts(void)
+{
+       /* IN interrupts on the control an IN endpoints */
+       USBIIE = (1 << AO_USB_CONTROL_EP) | (1 << AO_USB_IN_EP);
+
+       /* OUT interrupts on the OUT endpoint */
+       USBOIE = (1 << AO_USB_OUT_EP);
+
+       /* Only care about reset */
+       USBCIE = USBCIE_RSTIE;
+}
+
+/* This interrupt is shared with port 2,
+ * so when we hook that up, fix this
+ */
+void
+ao_usb_isr(void) __interrupt 6
+{
+       USBIF = 0;
+       ao_usb_iif |= USBIIF;
+       if (ao_usb_iif & 1)
+               ao_wakeup(&ao_usb_task);
+       if (ao_usb_iif & (1 << AO_USB_IN_EP))
+               ao_wakeup(&ao_usb_in_bytes);
+
+       if (USBOIF & (1 << AO_USB_OUT_EP))
+               ao_wakeup(&ao_stdin_ready);
+
+       if (USBCIF & USBCIF_RSTIF)
+               ao_usb_set_interrupts();
+#if HAS_BTM
+#if BT_LINK_ON_P2
+       ao_btm_isr();
+#endif
+#endif
+#if HAS_P2_ISR
+       ao_p2_isr();
+#endif
+}
+
+struct ao_usb_setup {
+       uint8_t         dir_type_recip;
+       uint8_t         request;
+       uint16_t        value;
+       uint16_t        index;
+       uint16_t        length;
+} __xdata ao_usb_setup;
+
+__pdata uint8_t ao_usb_ep0_state;
+uint8_t * __pdata ao_usb_ep0_in_data;
+__pdata uint8_t ao_usb_ep0_in_len;
+__pdata uint8_t        ao_usb_ep0_in_buf[2];
+__pdata uint8_t ao_usb_ep0_out_len;
+__xdata uint8_t *__pdata ao_usb_ep0_out_data;
+__pdata uint8_t ao_usb_configuration;
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+       __pdata uint8_t this_len;
+       __pdata uint8_t cs0;
+
+       /* If the IN packet hasn't been picked up, just return */
+       USBINDEX = 0;
+       cs0 = USBCS0;
+       if (cs0 & USBCS0_INPKT_RDY)
+               return;
+
+       this_len = ao_usb_ep0_in_len;
+       if (this_len > AO_USB_CONTROL_SIZE)
+               this_len = AO_USB_CONTROL_SIZE;
+       cs0 = USBCS0_INPKT_RDY;
+       if (this_len != AO_USB_CONTROL_SIZE) {
+               cs0 = USBCS0_INPKT_RDY | USBCS0_DATA_END;
+               ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       }
+       ao_usb_ep0_in_len -= this_len;
+       while (this_len--)
+               USBFIFO[0] = *ao_usb_ep0_in_data++;
+       USBINDEX = 0;
+       USBCS0 = cs0;
+}
+
+__xdata static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value)
+{
+       __code uint8_t          *__pdata descriptor;
+       __pdata uint8_t         type = value >> 8;
+       __pdata uint8_t         index = value;
+
+       descriptor = ao_usb_descriptors;
+       while (descriptor[0] != 0) {
+               if (descriptor[1] == type && index-- == 0) {
+                       if (type == AO_USB_DESC_CONFIGURATION)
+                               ao_usb_ep0_in_len = descriptor[2];
+                       else
+                               ao_usb_ep0_in_len = descriptor[0];
+                       ao_usb_ep0_in_data = descriptor;
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+/* Read data from the ep0 OUT fifo
+ */
+static void
+ao_usb_ep0_fill(void)
+{
+       __pdata uint8_t len;
+
+       USBINDEX = 0;
+       len = USBCNT0;
+       if (len > ao_usb_ep0_out_len)
+               len = ao_usb_ep0_out_len;
+       ao_usb_ep0_out_len -= len;
+       while (len--)
+               *ao_usb_ep0_out_data++ = USBFIFO[0];
+}
+
+void
+ao_usb_ep0_queue_byte(uint8_t a)
+{
+       ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+void
+ao_usb_set_address(uint8_t address)
+{
+       ao_usb_running = 1;
+       USBADDR = address | 0x80;
+       while (USBADDR & 0x80)
+               ;
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+       /* Set the IN max packet size, double buffered */
+       USBINDEX = AO_USB_IN_EP;
+       USBMAXI = AO_USB_IN_SIZE >> 3;
+       USBCSIH |= USBCSIH_IN_DBL_BUF;
+
+       /* Set the OUT max packet size, double buffered */
+       USBINDEX = AO_USB_OUT_EP;
+       USBMAXO = AO_USB_OUT_SIZE >> 3;
+       USBCSOH = USBCSOH_OUT_DBL_BUF;
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       /* Pull the setup packet out of the fifo */
+       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_setup;
+       ao_usb_ep0_out_len = 8;
+       ao_usb_ep0_fill();
+       if (ao_usb_ep0_out_len != 0)
+               return;
+
+       /* Figure out how to ACK the setup packet */
+       if (ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) {
+               if (ao_usb_setup.length)
+                       ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+               else
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       } else {
+               if (ao_usb_setup.length)
+                       ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+               else
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       }
+       USBINDEX = 0;
+       if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
+       else
+               USBCS0 = USBCS0_CLR_OUTPKT_RDY;
+
+       ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+       ao_usb_ep0_in_len = 0;
+       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+       case AO_USB_TYPE_STANDARD:
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               ao_usb_set_address(ao_usb_setup.value);
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               ao_usb_get_descriptor(ao_usb_setup.value);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               ao_usb_ep0_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = ao_usb_setup.value;
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       #pragma disable_warning 110
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_GET_INTERFACE:
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_INTERFACE:
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_ENDPOINT:
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_queue_byte(0);
+                               ao_usb_ep0_queue_byte(0);
+                               break;
+                       }
+                       break;
+               }
+               break;
+       case AO_USB_TYPE_CLASS:
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       ao_usb_ep0_out_len = 7;
+                       ao_usb_ep0_out_data = (__xdata uint8_t *) &ao_usb_line_coding;
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       ao_usb_ep0_in_len = 7;
+                       ao_usb_ep0_in_data = (uint8_t *) &ao_usb_line_coding;
+                       break;
+               case AO_USB_SET_CONTROL_LINE_STATE:
+                       break;
+               }
+               break;
+       }
+       if (ao_usb_ep0_state != AO_USB_EP0_DATA_OUT) {
+               if (ao_usb_setup.length < ao_usb_ep0_in_len)
+                       ao_usb_ep0_in_len = ao_usb_setup.length;
+               ao_usb_ep0_flush();
+       }
+}
+
+/* End point 0 receives all of the control messages. */
+static void
+ao_usb_ep0(void)
+{
+       __pdata uint8_t cs0;
+
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       for (;;) {
+               __critical for (;;) {
+                       if (ao_usb_iif & 1) {
+                               ao_usb_iif &= ~1;
+                               break;
+                       }
+                       ao_sleep(&ao_usb_task);
+               }
+               USBINDEX = 0;
+               cs0 = USBCS0;
+               if (cs0 & USBCS0_SETUP_END) {
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+                       USBCS0 = USBCS0_CLR_SETUP_END;
+               }
+               if (cs0 & USBCS0_SENT_STALL) {
+                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+                       USBCS0 &= ~USBCS0_SENT_STALL;
+               }
+               if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN &&
+                   (cs0 & USBCS0_INPKT_RDY) == 0)
+               {
+                       ao_usb_ep0_flush();
+               }
+               if (cs0 & USBCS0_OUTPKT_RDY) {
+                       switch (ao_usb_ep0_state) {
+                       case AO_USB_EP0_IDLE:
+                               ao_usb_ep0_setup();
+                               break;
+                       case AO_USB_EP0_DATA_OUT:
+                               ao_usb_ep0_fill();
+                               if (ao_usb_ep0_out_len == 0)
+                                       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+                               USBINDEX = 0;
+                               if (ao_usb_ep0_state == AO_USB_EP0_IDLE)
+                                       USBCS0 = USBCS0_CLR_OUTPKT_RDY | USBCS0_DATA_END;
+                               else
+                                       USBCS0 = USBCS0_CLR_OUTPKT_RDY;
+                               break;
+                       }
+               }
+       }
+}
+
+/* Wait for a free IN buffer */
+static void
+ao_usb_in_wait(void)
+{
+       for (;;) {
+               USBINDEX = AO_USB_IN_EP;
+               if ((USBCSIL & USBCSIL_INPKT_RDY) == 0)
+                       break;
+               ao_sleep(&ao_usb_in_bytes);
+       }
+}
+
+/* Send the current IN packet */
+static void
+ao_usb_in_send(void)
+{
+       USBINDEX = AO_USB_IN_EP;
+       USBCSIL |= USBCSIL_INPKT_RDY;
+       ao_usb_in_bytes_last = ao_usb_in_bytes;
+       ao_usb_in_bytes = 0;
+}
+
+void
+ao_usb_flush(void) __critical
+{
+       if (!ao_usb_running)
+               return;
+
+       /* If there are pending bytes, or if the last packet was full,
+        * send another IN packet
+        */
+       if (ao_usb_in_bytes || (ao_usb_in_bytes_last == AO_USB_IN_SIZE)) {
+               ao_usb_in_wait();
+               ao_usb_in_send();
+       }
+}
+
+void
+ao_usb_putchar(char c) __critical __reentrant
+{
+       if (!ao_usb_running)
+               return;
+
+       ao_usb_in_wait();
+
+       /* Queue a byte, sending the packet when full */
+       USBFIFO[AO_USB_IN_EP << 1] = c;
+       if (++ao_usb_in_bytes == AO_USB_IN_SIZE)
+               ao_usb_in_send();
+}
+
+char
+ao_usb_pollchar(void) __critical
+{
+       char c;
+       if (ao_usb_out_bytes == 0) {
+               USBINDEX = AO_USB_OUT_EP;
+               if ((USBCSOL & USBCSOL_OUTPKT_RDY) == 0)
+                       return AO_READ_AGAIN;
+               ao_usb_out_bytes = (USBCNTH << 8) | USBCNTL;
+               if (ao_usb_out_bytes == 0) {
+                       USBINDEX = AO_USB_OUT_EP;
+                       USBCSOL &= ~USBCSOL_OUTPKT_RDY;
+                       return AO_READ_AGAIN;
+               }
+       }
+       --ao_usb_out_bytes;
+       c = USBFIFO[AO_USB_OUT_EP << 1];
+       if (ao_usb_out_bytes == 0) {
+               USBINDEX = AO_USB_OUT_EP;
+               USBCSOL &= ~USBCSOL_OUTPKT_RDY;
+       }
+       return c;
+}
+
+char
+ao_usb_getchar(void) __critical
+{
+       char    c;
+
+       while ((c = ao_usb_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(&ao_stdin_ready);
+       return c;
+}
+
+void
+ao_usb_enable(void)
+{
+       /* Turn on the USB controller */
+       SLEEP |= SLEEP_USB_EN;
+
+       ao_usb_set_configuration();
+
+       ao_usb_set_interrupts();
+
+       /* enable USB interrupts */
+       IEN2 |= IEN2_USBIE;
+
+       /* Clear any pending interrupts */
+       USBCIF = 0;
+       USBOIF = 0;
+       USBIIF = 0;
+}
+
+void
+ao_usb_disable(void)
+{
+       /* Disable USB interrupts */
+       USBIIE = 0;
+       USBOIE = 0;
+       USBCIE = 0;
+       IEN2 &= ~IEN2_USBIE;
+
+       /* Clear any pending interrupts */
+       USBCIF = 0;
+       USBOIF = 0;
+       USBIIF = 0;
+
+       /* Turn off the USB controller */
+       SLEEP &= ~SLEEP_USB_EN;
+}
+
+void
+ao_usb_init(void)
+{
+       ao_usb_enable();
+
+       ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");
+       ao_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+}
diff --git a/src/cc1111/cc1111.h b/src/cc1111/cc1111.h
new file mode 100644 (file)
index 0000000..80d3fb7
--- /dev/null
@@ -0,0 +1,1328 @@
+/*-------------------------------------------------------------------------
+   Register Declarations for the ChipCon CC1111 Processor Range
+
+   Copyright © 2008 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.
+
+   Adapted from the Cygnal C8051F12x config file which is:
+
+   Copyright (C) 2003 - Maarten Brock, sourceforge.brock@dse.nl
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+-------------------------------------------------------------------------*/
+
+#ifndef _CC1111_H_
+#define _CC1111_H_
+#include <cc1110.h>
+#include <stdint.h>
+
+__sfr __at 0xA8 IEN0;          /* Interrupt Enable 0 Register */
+
+__sbit __at 0xA8 RFTXRXIE;     /* RF TX/RX done interrupt enable */
+__sbit __at 0xA9 ADCIE;                /* ADC interrupt enable */
+__sbit __at 0xAA URX0IE;               /* USART0 RX interrupt enable */
+__sbit __at 0xAB URX1IE;               /* USART1 RX interrupt enable (shared with I2S RX) */
+__sbit __at 0xAB I2SRXIE;              /* I2S RX interrupt enable (shared with USART1 RX) */
+__sbit __at 0xAC ENCIE;                /* AES encryption/decryption interrupt enable */
+__sbit __at 0xAD STIE;         /* Sleep Timer interrupt enable */
+__sbit __at 0xAF EA;           /* Enable All */
+
+#define IEN0_EA                        (1 << 7)
+#define IEN0_STIE              (1 << 5)
+#define IEN0_ENCIE             (1 << 4)
+#define IEN0_URX1IE            (1 << 3)
+#define IEN0_I2SRXIE           (1 << 3)
+#define IEN0_URX0IE            (1 << 2)
+#define IEN0_ADCIE             (1 << 1)
+#define IEN0_RFTXRXIE          (1 << 0)
+
+__sfr __at 0xB8 IEN1;          /* Interrupt Enable 1 Register */
+
+#define IEN1_P0IE              (1 << 5)        /* Port 0 interrupt enable */
+#define IEN1_T4IE              (1 << 4)        /* Timer 4 interrupt enable */
+#define IEN1_T3IE              (1 << 3)        /* Timer 3 interrupt enable */
+#define IEN1_T2IE              (1 << 2)        /* Timer 2 interrupt enable */
+#define IEN1_T1IE              (1 << 1)        /* Timer 1 interrupt enable */
+#define IEN1_DMAIE             (1 << 0)        /* DMA transfer interrupt enable */
+
+/* IEN2 */
+__sfr __at 0x9A IEN2;          /* Interrupt Enable 2 Register */
+
+#define IEN2_WDTIE             (1 << 5)        /* Watchdog timer interrupt enable */
+#define IEN2_P1IE              (1 << 4)        /* Port 1 interrupt enable */
+#define IEN2_UTX1IE            (1 << 3)        /* USART1 TX interrupt enable */
+#define IEN2_I2STXIE           (1 << 3)        /* I2S TX interrupt enable */
+#define IEN2_UTX0IE            (1 << 2)        /* USART0 TX interrupt enable */
+#define IEN2_P2IE              (1 << 1)        /* Port 2 interrupt enable */
+#define IEN2_USBIE             (1 << 1)        /* USB interrupt enable */
+#define IEN2_RFIE              (1 << 0)        /* RF general interrupt enable */
+
+/* CLKCON 0xC6 */
+__sfr __at 0xC6 CLKCON;                /* Clock Control */
+
+#define CLKCON_OSC32K_RC       (1 << 7)
+#define CLKCON_OSC32K_XTAL     (0 << 7)
+#define CLKCON_OSC32K_MASK     (1 << 7)
+#define CLKCON_OSC_RC          (1 << 6)
+#define CLKCON_OSC_XTAL                (0 << 6)
+#define CLKCON_OSC_MASK                (1 << 6)
+#define CLKCON_TICKSPD_MASK    (7 << 3)
+# define CLKCON_TICKSPD_1      (0 << 3)
+# define CLKCON_TICKSPD_1_2    (1 << 3)
+# define CLKCON_TICKSPD_1_4    (2 << 3)
+# define CLKCON_TICKSPD_1_8    (3 << 3)
+# define CLKCON_TICKSPD_1_16   (4 << 3)
+# define CLKCON_TICKSPD_1_32   (5 << 3)
+# define CLKCON_TICKSPD_1_64   (6 << 3)
+# define CLKCON_TICKSPD_1_128  (7 << 3)
+
+#define CLKCON_CLKSPD_MASK     (7 << 0)
+# define CLKCON_CLKSPD_1       (0 << 0)
+# define CLKCON_CLKSPD_1_2     (1 << 0)
+# define CLKCON_CLKSPD_1_4     (2 << 0)
+# define CLKCON_CLKSPD_1_8     (3 << 0)
+# define CLKCON_CLKSPD_1_16    (4 << 0)
+# define CLKCON_CLKSPD_1_32    (5 << 0)
+# define CLKCON_CLKSPD_1_64    (6 << 0)
+# define CLKCON_CLKSPD_1_128   (7 << 0)
+
+/* SLEEP 0xBE */
+#define SLEEP_USB_EN           (1 << 7)
+#define SLEEP_XOSC_STB         (1 << 6)
+#define SLEEP_HFRC_STB         (1 << 5)
+#define SLEEP_RST_POWER                (0 << 3)
+#define SLEEP_RST_EXTERNAL     (1 << 3)
+#define SLEEP_RST_WATCHDOG     (2 << 3)
+#define SLEEP_RST_MASK         (3 << 3)
+#define SLEEP_OSC_PD           (1 << 2)
+#define SLEEP_MODE_PM0         (0 << 0)
+#define SLEEP_MODE_PM1         (1 << 0)
+#define SLEEP_MODE_PM2         (2 << 0)
+#define SLEEP_MODE_PM3         (3 << 0)
+#define SLEEP_MODE_MASK                (3 << 0)
+
+/* PCON 0x87 */
+__sfr __at 0x87 PCON;          /* Power Mode Control Register */
+
+#define PCON_IDLE              (1 << 0)
+
+/*
+ * TCON
+ */
+__sfr __at 0x88 TCON;          /* CPU Interrupt Flag 1 */
+
+__sbit __at 0x8F URX1IF;               /* USART1 RX interrupt flag. Automatically cleared */
+__sbit __at 0x8F I2SRXIF;              /* I2S RX interrupt flag. Automatically cleared */
+__sbit __at 0x8D ADCIF;                /* ADC interrupt flag. Automatically cleared */
+__sbit __at 0x8B URX0IF;               /* USART0 RX interrupt flag. Automatically cleared */
+__sbit __at 0x89 RFTXRXIF;     /* RF TX/RX complete interrupt flag. Automatically cleared */
+
+#define TCON_URX1IF    (1 << 7)
+#define TCON_I2SRXIF   (1 << 7)
+#define TCON_ADCIF     (1 << 5)
+#define TCON_URX0IF    (1 << 3)
+#define TCON_RFTXRXIF  (1 << 1)
+
+/*
+ * S0CON
+ */
+__sfr __at 0x98 S0CON; /* CPU Interrupt Flag 2 */
+
+__sbit __at 0x98 ENCIF_0;      /* AES interrupt 0. */
+__sbit __at 0x99 ENCIF_1;      /* AES interrupt 1. */
+
+#define S0CON_ENCIF_1  (1 << 1)
+#define S0CON_ENCIF_0  (1 << 0)
+
+/*
+ * S1CON
+ */
+__sfr __at 0x9B S1CON; /* CPU Interrupt Flag 3 */
+
+#define S1CON_RFIF_1   (1 << 1)
+#define S1CON_RFIF_0   (1 << 0)
+
+/*
+ * IRCON
+ */
+__sfr __at 0xC0 IRCON; /* CPU Interrupt Flag 4 */
+
+__sbit __at 0xC0 DMAIF;        /* DMA complete interrupt flag */
+__sbit __at 0xC1 T1IF; /* Timer 1 interrupt flag. Automatically cleared */
+__sbit __at 0xC2 T2IF; /* Timer 2 interrupt flag. Automatically cleared */
+__sbit __at 0xC3 T3IF; /* Timer 3 interrupt flag. Automatically cleared */
+__sbit __at 0xC4 T4IF; /* Timer 4 interrupt flag. Automatically cleared */
+__sbit __at 0xC5 P0IF; /* Port0 interrupt flag */
+__sbit __at 0xC7 STIF; /* Sleep Timer interrupt flag */
+
+#define IRCON_DMAIF    (1 << 0)        /* DMA complete interrupt flag */
+#define IRCON_T1IF     (1 << 1)        /* Timer 1 interrupt flag. Automatically cleared */
+#define IRCON_T2IF     (1 << 2)        /* Timer 2 interrupt flag. Automatically cleared */
+#define IRCON_T3IF     (1 << 3)        /* Timer 3 interrupt flag. Automatically cleared */
+#define IRCON_T4IF     (1 << 4)        /* Timer 4 interrupt flag. Automatically cleared */
+#define IRCON_P0IF     (1 << 5)        /* Port0 interrupt flag */
+#define IRCON_STIF     (1 << 7)        /* Sleep Timer interrupt flag */
+
+/*
+ * IRCON2
+ */
+__sfr __at 0xE8 IRCON2;        /* CPU Interrupt Flag 5 */
+
+__sbit __at 0xE8 USBIF;        /* USB interrupt flag (shared with Port2) */
+__sbit __at 0xE8 P2IF; /* Port2 interrupt flag (shared with USB) */
+__sbit __at 0xE9 UTX0IF;       /* USART0 TX interrupt flag */
+__sbit __at 0xEA UTX1IF;       /* USART1 TX interrupt flag (shared with I2S TX) */
+__sbit __at 0xEA I2STXIF;      /* I2S TX interrupt flag (shared with USART1 TX) */
+__sbit __at 0xEB P1IF; /* Port1 interrupt flag */
+__sbit __at 0xEC WDTIF;        /* Watchdog timer interrupt flag */
+
+#define IRCON2_USBIF   (1 << 0)        /* USB interrupt flag (shared with Port2) */
+#define IRCON2_P2IF    (1 << 0)        /* Port2 interrupt flag (shared with USB) */
+#define IRCON2_UTX0IF  (1 << 1)        /* USART0 TX interrupt flag */
+#define IRCON2_UTX1IF  (1 << 2)        /* USART1 TX interrupt flag (shared with I2S TX) */
+#define IRCON2_I2STXIF (1 << 2)        /* I2S TX interrupt flag (shared with USART1 TX) */
+#define IRCON2_P1IF    (1 << 3)        /* Port1 interrupt flag */
+#define IRCON2_WDTIF   (1 << 4)        /* Watchdog timer interrupt flag */
+
+/*
+ * IP1 - Interrupt Priority 1
+ */
+
+/*
+ * Interrupt priority groups:
+ *
+ * IPG0                RFTXRX          RF              DMA
+ * IPG1                ADC             T1              P2INT/USB
+ * IPG2                URX0            T2              UTX0
+ * IPG3                URX1/I2SRX      T3              UTX1 / I2STX
+ * IPG4                ENC             T4              P1INT
+ * IPG5                ST              P0INT           WDT
+ *
+ * Priority = (IP1 << 1) | IP0. Higher priority interrupts served first
+ */
+
+__sfr __at 0xB9 IP1;   /* Interrupt Priority 1 */
+__sfr __at 0xA9 IP0;   /* Interrupt Priority 0 */
+
+#define IP1_IPG5       (1 << 5)
+#define IP1_IPG4       (1 << 4)
+#define IP1_IPG3       (1 << 3)
+#define IP1_IPG2       (1 << 2)
+#define IP1_IPG1       (1 << 1)
+#define IP1_IPG0       (1 << 0)
+
+#define IP0_IPG5       (1 << 5)
+#define IP0_IPG4       (1 << 4)
+#define IP0_IPG3       (1 << 3)
+#define IP0_IPG2       (1 << 2)
+#define IP0_IPG1       (1 << 1)
+#define IP0_IPG0       (1 << 0)
+
+/*
+ * Timer 1
+ */
+#define T1CTL_MODE_SUSPENDED   (0 << 0)
+#define T1CTL_MODE_FREE                (1 << 0)
+#define T1CTL_MODE_MODULO      (2 << 0)
+#define T1CTL_MODE_UP_DOWN     (3 << 0)
+#define T1CTL_MODE_MASK                (3 << 0)
+#define T1CTL_DIV_1            (0 << 2)
+#define T1CTL_DIV_8            (1 << 2)
+#define T1CTL_DIV_32           (2 << 2)
+#define T1CTL_DIV_128          (3 << 2)
+#define T1CTL_DIV_MASK         (3 << 2)
+#define T1CTL_OVFIF            (1 << 4)
+#define T1CTL_CH0IF            (1 << 5)
+#define T1CTL_CH1IF            (1 << 6)
+#define T1CTL_CH2IF            (1 << 7)
+
+#define T1CCTL_NO_CAPTURE      (0 << 0)
+#define T1CCTL_CAPTURE_RISING  (1 << 0)
+#define T1CCTL_CAPTURE_FALLING (2 << 0)
+#define T1CCTL_CAPTURE_BOTH    (3 << 0)
+#define T1CCTL_CAPTURE_MASK    (3 << 0)
+
+#define T1CCTL_MODE_CAPTURE    (0 << 2)
+#define T1CCTL_MODE_COMPARE    (1 << 2)
+
+#define T1CTL_CMP_SET          (0 << 3)
+#define T1CTL_CMP_CLEAR                (1 << 3)
+#define T1CTL_CMP_TOGGLE       (2 << 3)
+#define T1CTL_CMP_SET_CLEAR    (3 << 3)
+#define T1CTL_CMP_CLEAR_SET    (4 << 3)
+
+#define T1CTL_IM_DISABLED      (0 << 6)
+#define T1CTL_IM_ENABLED       (1 << 6)
+
+#define T1CTL_CPSEL_NORMAL     (0 << 7)
+#define T1CTL_CPSEL_RF         (1 << 7)
+
+/*
+ * Timer 3 and Timer 4
+ */
+
+/* Timer count */
+__sfr __at 0xCA T3CNT;
+__sfr __at 0xEA T4CNT;
+
+/* Timer control */
+
+__sfr __at 0xCB T3CTL;
+__sfr __at 0xEB T4CTL;
+
+#define TxCTL_DIV_1            (0 << 5)
+#define TxCTL_DIV_2            (1 << 5)
+#define TxCTL_DIV_4            (2 << 5)
+#define TxCTL_DIV_8            (3 << 5)
+#define TxCTL_DIV_16           (4 << 5)
+#define TxCTL_DIV_32           (5 << 5)
+#define TxCTL_DIV_64           (6 << 5)
+#define TxCTL_DIV_128          (7 << 5)
+#define TxCTL_START            (1 << 4)
+#define TxCTL_OVFIM            (1 << 3)
+#define TxCTL_CLR              (1 << 2)
+#define TxCTL_MODE_FREE                (0 << 0)
+#define TxCTL_MODE_DOWN                (1 << 0)
+#define TxCTL_MODE_MODULO      (2 << 0)
+#define TxCTL_MODE_UP_DOWN     (3 << 0)
+
+/* Timer 4 channel 0 compare control */
+
+__sfr __at 0xCC T3CCTL0;
+__sfr __at 0xCE T3CCTL1;
+__sfr __at 0xEC T4CCTL0;
+__sfr __at 0xEE T4CCTL1;
+
+#define TxCCTLy_IM                     (1 << 6)
+#define TxCCTLy_CMP_SET                        (0 << 3)
+#define TxCCTLy_CMP_CLEAR              (1 << 3)
+#define TxCCTLy_CMP_TOGGLE             (2 << 3)
+#define TxCCTLy_CMP_SET_UP_CLEAR_DOWN  (3 << 3)
+#define TxCCTLy_CMP_CLEAR_UP_SET_DOWN  (4 << 3)
+#define TxCCTLy_CMP_SET_CLEAR_FF       (5 << 3)
+#define TxCCTLy_CMP_CLEAR_SET_00       (6 << 3)
+#define TxCCTLy_CMP_MODE_ENABLE                (1 << 2)
+
+/* Timer compare value */
+__sfr __at 0xCD T3CC0;
+__sfr __at 0xCF T3CC1;
+__sfr __at 0xED T4CC0;
+__sfr __at 0xEF T4CC1;
+
+/*
+ * Peripheral control
+ */
+
+__sfr __at 0xf1 PERCFG;
+#define PERCFG_T1CFG_ALT_1      (0 << 6)
+#define PERCFG_T1CFG_ALT_2      (1 << 6)
+#define PERCFG_T1CFG_ALT_MASK   (1 << 6)
+
+#define PERCFG_T3CFG_ALT_1      (0 << 5)
+#define PERCFG_T3CFG_ALT_2      (1 << 5)
+#define PERCFG_T3CFG_ALT_MASK   (1 << 5)
+
+#define PERCFG_T4CFG_ALT_1      (0 << 4)
+#define PERCFG_T4CFG_ALT_2      (1 << 4)
+#define PERCFG_T4CFG_ALT_MASK   (1 << 4)
+
+#define PERCFG_U1CFG_ALT_1      (0 << 1)
+#define PERCFG_U1CFG_ALT_2      (1 << 1)
+#define PERCFG_U1CFG_ALT_MASK   (1 << 1)
+
+#define PERCFG_U0CFG_ALT_1      (0 << 0)
+#define PERCFG_U0CFG_ALT_2      (1 << 0)
+#define PERCFG_U0CFG_ALT_MASK   (1 << 0)
+
+/* directly addressed USB registers */
+__xdata __at (0xde00) volatile uint8_t USBADDR;
+__xdata __at (0xde01) volatile uint8_t USBPOW;
+__xdata __at (0xde02) volatile uint8_t USBIIF;
+
+__xdata __at (0xde04) volatile uint8_t USBOIF;
+
+__xdata __at (0xde06) volatile uint8_t USBCIF;
+
+# define USBCIF_SOFIF          (1 << 3)
+# define USBCIF_RSTIF          (1 << 2)
+# define USBCIF_RESUMEIF       (1 << 1)
+# define USBCIF_SUSPENDIF      (1 << 0)
+
+__xdata __at (0xde07) volatile uint8_t USBIIE;
+
+__xdata __at (0xde09) volatile uint8_t USBOIE;
+
+__xdata __at (0xde0b) volatile uint8_t USBCIE;
+
+# define USBCIE_SOFIE          (1 << 3)
+# define USBCIE_RSTIE          (1 << 2)
+# define USBCIE_RESUMEIE       (1 << 1)
+# define USBCIE_SUSPENDIE      (1 << 0)
+
+__xdata __at (0xde0c) volatile uint8_t USBFRML;
+__xdata __at (0xde0d) volatile uint8_t USBFRMH;
+__xdata __at (0xde0e) volatile uint8_t USBINDEX;
+
+/* indexed USB registers, must set USBINDEX to 0-5 */
+__xdata __at (0xde10) volatile uint8_t USBMAXI;
+__xdata __at (0xde11) volatile uint8_t USBCS0;
+
+# define USBCS0_CLR_SETUP_END          (1 << 7)
+# define USBCS0_CLR_OUTPKT_RDY         (1 << 6)
+# define USBCS0_SEND_STALL             (1 << 5)
+# define USBCS0_SETUP_END              (1 << 4)
+# define USBCS0_DATA_END               (1 << 3)
+# define USBCS0_SENT_STALL             (1 << 2)
+# define USBCS0_INPKT_RDY              (1 << 1)
+# define USBCS0_OUTPKT_RDY             (1 << 0)
+
+__xdata __at (0xde11) volatile uint8_t USBCSIL;
+
+# define USBCSIL_CLR_DATA_TOG          (1 << 6)
+# define USBCSIL_SENT_STALL            (1 << 5)
+# define USBCSIL_SEND_STALL            (1 << 4)
+# define USBCSIL_FLUSH_PACKET          (1 << 3)
+# define USBCSIL_UNDERRUN              (1 << 2)
+# define USBCSIL_PKT_PRESENT           (1 << 1)
+# define USBCSIL_INPKT_RDY             (1 << 0)
+
+__xdata __at (0xde12) volatile uint8_t USBCSIH;
+
+# define USBCSIH_AUTOSET               (1 << 7)
+# define USBCSIH_ISO                   (1 << 6)
+# define USBCSIH_FORCE_DATA_TOG                (1 << 3)
+# define USBCSIH_IN_DBL_BUF            (1 << 0)
+
+__xdata __at (0xde13) volatile uint8_t USBMAXO;
+__xdata __at (0xde14) volatile uint8_t USBCSOL;
+
+# define USBCSOL_CLR_DATA_TOG          (1 << 7)
+# define USBCSOL_SENT_STALL            (1 << 6)
+# define USBCSOL_SEND_STALL            (1 << 5)
+# define USBCSOL_FLUSH_PACKET          (1 << 4)
+# define USBCSOL_DATA_ERROR            (1 << 3)
+# define USBCSOL_OVERRUN               (1 << 2)
+# define USBCSOL_FIFO_FULL             (1 << 1)
+# define USBCSOL_OUTPKT_RDY            (1 << 0)
+
+__xdata __at (0xde15) volatile uint8_t USBCSOH;
+
+# define USBCSOH_AUTOCLEAR             (1 << 7)
+# define USBCSOH_ISO                   (1 << 6)
+# define USBCSOH_OUT_DBL_BUF           (1 << 0)
+
+__xdata __at (0xde16) volatile uint8_t USBCNT0;
+__xdata __at (0xde16) volatile uint8_t USBCNTL;
+__xdata __at (0xde17) volatile uint8_t USBCNTH;
+
+__xdata __at (0xde20) volatile uint8_t USBFIFO[12];
+
+/* ADC Data register, low and high */
+__sfr __at 0xBA ADCL;
+__sfr __at 0xBB ADCH;
+__xdata __at (0xDFBA) volatile uint16_t ADCXDATA;
+
+/* ADC Control Register 1 */
+__sfr __at 0xB4 ADCCON1;
+
+# define ADCCON1_EOC           (1 << 7)        /* conversion complete */
+# define ADCCON1_ST            (1 << 6)        /* start conversion */
+
+# define ADCCON1_STSEL_MASK    (3 << 4)        /* start select */
+# define ADCCON1_STSEL_EXTERNAL        (0 << 4)        /* P2_0 pin triggers */
+# define ADCCON1_STSEL_FULLSPEED (1 << 4)      /* full speed, no waiting */
+# define ADCCON1_STSEL_TIMER1  (2 << 4)        /* timer 1 channel 0 */
+# define ADCCON1_STSEL_START   (3 << 4)        /* set start bit */
+
+# define ADCCON1_RCTRL_MASK    (3 << 2)        /* random number control */
+# define ADCCON1_RCTRL_COMPLETE        (0 << 2)        /* operation completed */
+# define ADCCON1_RCTRL_CLOCK_LFSR (1 << 2)     /* Clock the LFSR once */
+
+/* ADC Control Register 2 */
+__sfr __at 0xB5 ADCCON2;
+
+# define ADCCON2_SREF_MASK     (3 << 6)        /* reference voltage */
+# define ADCCON2_SREF_1_25V    (0 << 6)        /* internal 1.25V */
+# define ADCCON2_SREF_EXTERNAL (1 << 6)        /* external on AIN7 cc1110 */
+# define ADCCON2_SREF_VDD      (2 << 6)        /* VDD on the AVDD pin */
+# define ADCCON2_SREF_EXTERNAL_DIFF (3 << 6)   /* external on AIN6-7 cc1110 */
+
+# define ADCCON2_SDIV_MASK     (3 << 4)        /* decimation rate */
+# define ADCCON2_SDIV_64       (0 << 4)        /* 7 bits */
+# define ADCCON2_SDIV_128      (1 << 4)        /* 9 bits */
+# define ADCCON2_SDIV_256      (2 << 4)        /* 10 bits */
+# define ADCCON2_SDIV_512      (3 << 4)        /* 12 bits */
+
+# define ADCCON2_SCH_MASK      (0xf << 0)      /* Sequence channel select */
+# define ADCCON2_SCH_SHIFT     0
+# define ADCCON2_SCH_AIN0      (0 << 0)
+# define ADCCON2_SCH_AIN1      (1 << 0)
+# define ADCCON2_SCH_AIN2      (2 << 0)
+# define ADCCON2_SCH_AIN3      (3 << 0)
+# define ADCCON2_SCH_AIN4      (4 << 0)
+# define ADCCON2_SCH_AIN5      (5 << 0)
+# define ADCCON2_SCH_AIN6      (6 << 0)
+# define ADCCON2_SCH_AIN7      (7 << 0)
+# define ADCCON2_SCH_AIN0_AIN1 (8 << 0)
+# define ADCCON2_SCH_AIN2_AIN3 (9 << 0)
+# define ADCCON2_SCH_AIN4_AIN5 (0xa << 0)
+# define ADCCON2_SCH_AIN6_AIN7 (0xb << 0)
+# define ADCCON2_SCH_GND       (0xc << 0)
+# define ADCCON2_SCH_VREF      (0xd << 0)
+# define ADCCON2_SCH_TEMP      (0xe << 0)
+# define ADCCON2_SCH_VDD_3     (0xf << 0)
+
+
+/* ADC Control Register 3 */
+__sfr __at 0xB6 ADCCON3;
+
+# define ADCCON3_EREF_MASK     (3 << 6)        /* extra conversion reference */
+# define ADCCON3_EREF_1_25     (0 << 6)        /* internal 1.25V */
+# define ADCCON3_EREF_EXTERNAL (1 << 6)        /* external AIN7 cc1110 */
+# define ADCCON3_EREF_VDD      (2 << 6)        /* VDD on the AVDD pin */
+# define ADCCON3_EREF_EXTERNAL_DIFF (3 << 6)   /* external AIN6-7 cc1110 */
+# define ADCCON3_EDIV_MASK     (3 << 4)        /* extral decimation */
+# define ADCCON3_EDIV_64       (0 << 4)        /* 7 bits */
+# define ADCCON3_EDIV_128      (1 << 4)        /* 9 bits */
+# define ADCCON3_EDIV_256      (2 << 4)        /* 10 bits */
+# define ADCCON3_EDIV_512      (3 << 4)        /* 12 bits */
+# define ADCCON3_ECH_MASK      (0xf << 0)      /* Sequence channel select */
+# define ADCCON3_ECH_SHIFT     0
+# define ADCCON3_ECH_AIN0      (0 << 0)
+# define ADCCON3_ECH_AIN1      (1 << 0)
+# define ADCCON3_ECH_AIN2      (2 << 0)
+# define ADCCON3_ECH_AIN3      (3 << 0)
+# define ADCCON3_ECH_AIN4      (4 << 0)
+# define ADCCON3_ECH_AIN5      (5 << 0)
+# define ADCCON3_ECH_AIN6      (6 << 0)
+# define ADCCON3_ECH_AIN7      (7 << 0)
+# define ADCCON3_ECH_AIN0_AIN1 (8 << 0)
+# define ADCCON3_ECH_AIN2_AIN3 (9 << 0)
+# define ADCCON3_ECH_AIN4_AIN5 (0xa << 0)
+# define ADCCON3_ECH_AIN6_AIN7 (0xb << 0)
+# define ADCCON3_ECH_GND       (0xc << 0)
+# define ADCCON3_ECH_VREF      (0xd << 0)
+# define ADCCON3_ECH_TEMP      (0xe << 0)
+# define ADCCON3_ECH_VDD_3     (0xf << 0)
+
+/*
+ * ADC configuration register, this selects which
+ * GPIO pins are to be used as ADC inputs
+ */
+__sfr __at 0xF2 ADCCFG;
+
+/*
+ * Watchdog timer
+ */
+
+__sfr __at 0xc9 WDCTL;
+
+#define WDCTL_CLEAR_FIRST      (0xa << 4)
+#define WDCTL_CLEAR_SECOND     (0x5 << 4)
+#define WDCTL_EN               (1 << 3)
+#define WDCTL_MODE_WATCHDOG    (0 << 2)
+#define WDCTL_MODE_TIMER       (1 << 2)
+#define WDCTL_MODE_MASK                (1 << 2)
+#define WDCTL_INT_32768                (0 << 0)
+#define WDCTL_INT_8192         (1 << 0)
+#define WDCTL_INT_512          (2 << 0)
+#define WDCTL_INT_64           (3 << 0)
+
+/*
+ * Pin selectors, these set which pins are
+ * using their peripheral function
+ */
+__sfr __at 0xF3 P0SEL;
+__sfr __at 0xF4 P1SEL;
+__sfr __at 0xF5 P2SEL;
+
+#define P2SEL_PRI3P1_USART0            (0 << 6)
+#define P2SEL_PRI3P1_USART1            (1 << 6)
+#define P2SEL_PRI3P1_MASK              (1 << 6)
+#define P2SEL_PRI2P1_USART1            (0 << 5)
+#define P2SEL_PRI2P1_TIMER3            (1 << 5)
+#define P2SEL_PRI2P1_MASK              (1 << 5)
+#define P2SEL_PRI1P1_TIMER1            (0 << 4)
+#define P2SEL_PRI1P1_TIMER4            (1 << 4)
+#define P2SEL_PRI1P1_MASK              (1 << 4)
+#define P2SEL_PRI0P1_USART0            (0 << 3)
+#define P2SEL_PRI0P1_TIMER1            (1 << 3)
+#define P2SEL_PRI0P1_MASK              (1 << 3)
+#define P2SEL_SELP2_4_GPIO             (0 << 2)
+#define P2SEL_SELP2_4_PERIPHERAL       (1 << 2)
+#define P2SEL_SELP2_4_MASK             (1 << 2)
+#define P2SEL_SELP2_3_GPIO             (0 << 1)
+#define P2SEL_SELP2_3_PERIPHERAL       (1 << 1)
+#define P2SEL_SELP2_3_MASK             (1 << 1)
+#define P2SEL_SELP2_0_GPIO             (0 << 0)
+#define P2SEL_SELP2_0_PERIPHERAL       (1 << 0)
+#define P2SEL_SELP2_0_MASK             (1 << 0)
+
+/*
+ * For pins used as GPIOs, these set which are used as outputs
+ */
+__sfr __at 0xFD P0DIR;
+__sfr __at 0xFE P1DIR;
+__sfr __at 0xFF P2DIR;
+
+#define P2DIR_PRIP0_USART0_USART1      (0 << 6)
+#define P2DIR_PRIP0_USART1_USART0      (1 << 6)
+#define P2DIR_PRIP0_TIMER1_01_USART1   (2 << 6)
+#define P2DIR_PRIP0_TIMER1_2_USART0    (3 << 6)
+#define P2DIR_PRIP0_MASK               (3 << 6)
+
+__sfr __at 0x8F P0INP;
+
+/* Select between tri-state and pull up/down
+ * for pins P0_0 - P0_7.
+ */
+#define P0INP_MDP0_7_PULL      (0 << 7)
+#define P0INP_MDP0_7_TRISTATE  (1 << 7)
+#define P0INP_MDP0_6_PULL      (0 << 6)
+#define P0INP_MDP0_6_TRISTATE  (1 << 6)
+#define P0INP_MDP0_5_PULL      (0 << 5)
+#define P0INP_MDP0_5_TRISTATE  (1 << 5)
+#define P0INP_MDP0_4_PULL      (0 << 4)
+#define P0INP_MDP0_4_TRISTATE  (1 << 4)
+#define P0INP_MDP0_3_PULL      (0 << 3)
+#define P0INP_MDP0_3_TRISTATE  (1 << 3)
+#define P0INP_MDP0_2_PULL      (0 << 2)
+#define P0INP_MDP0_2_TRISTATE  (1 << 2)
+#define P0INP_MDP0_1_PULL      (0 << 1)
+#define P0INP_MDP0_1_TRISTATE  (1 << 1)
+#define P0INP_MDP0_0_PULL      (0 << 0)
+#define P0INP_MDP0_0_TRISTATE  (1 << 0)
+
+__sfr __at 0xF6 P1INP;
+
+/* Select between tri-state and pull up/down
+ * for pins P1_2 - P1_7. Pins P1_0 and P1_1 are
+ * always tri-stated
+ */
+#define P1INP_MDP1_7_PULL      (0 << 7)
+#define P1INP_MDP1_7_TRISTATE  (1 << 7)
+#define P1INP_MDP1_6_PULL      (0 << 6)
+#define P1INP_MDP1_6_TRISTATE  (1 << 6)
+#define P1INP_MDP1_5_PULL      (0 << 5)
+#define P1INP_MDP1_5_TRISTATE  (1 << 5)
+#define P1INP_MDP1_4_PULL      (0 << 4)
+#define P1INP_MDP1_4_TRISTATE  (1 << 4)
+#define P1INP_MDP1_3_PULL      (0 << 3)
+#define P1INP_MDP1_3_TRISTATE  (1 << 3)
+#define P1INP_MDP1_2_PULL      (0 << 2)
+#define P1INP_MDP1_2_TRISTATE  (1 << 2)
+
+__sfr __at 0xF7 P2INP;
+/* P2INP has three extra bits which are used to choose
+ * between pull-up and pull-down when they are not tri-stated
+ */
+#define P2INP_PDUP2_PULL_UP    (0 << 7)
+#define P2INP_PDUP2_PULL_DOWN  (1 << 7)
+#define P2INP_PDUP1_PULL_UP    (0 << 6)
+#define P2INP_PDUP1_PULL_DOWN  (1 << 6)
+#define P2INP_PDUP0_PULL_UP    (0 << 5)
+#define P2INP_PDUP0_PULL_DOWN  (1 << 5)
+
+/* For the P2 pins, choose between tri-state and pull up/down
+ * mode
+ */
+#define P2INP_MDP2_4_PULL      (0 << 4)
+#define P2INP_MDP2_4_TRISTATE  (1 << 4)
+#define P2INP_MDP2_3_PULL      (0 << 3)
+#define P2INP_MDP2_3_TRISTATE  (1 << 3)
+#define P2INP_MDP2_2_PULL      (0 << 2)
+#define P2INP_MDP2_2_TRISTATE  (1 << 2)
+#define P2INP_MDP2_1_PULL      (0 << 1)
+#define P2INP_MDP2_1_TRISTATE  (1 << 1)
+#define P2INP_MDP2_0_PULL      (0 << 0)
+#define P2INP_MDP2_0_TRISTATE  (1 << 0)
+
+/* GPIO interrupt status flags */
+__sfr __at 0x89 P0IFG;
+__sfr __at 0x8A P1IFG;
+__sfr __at 0x8B P2IFG;
+
+#define P0IFG_USB_RESUME       (1 << 7)
+
+__sfr __at 0x8C PICTL;
+#define PICTL_P2IEN    (1 << 5)
+#define PICTL_P0IENH   (1 << 4)
+#define PICTL_P0IENL   (1 << 3)
+#define PICTL_P2ICON   (1 << 2)
+#define PICTL_P1ICON   (1 << 1)
+#define PICTL_P0ICON   (1 << 0)
+
+/* GPIO pins */
+__sfr __at 0x80 P0;
+
+__sbit __at 0x80 P0_0;
+__sbit __at 0x81 P0_1;
+__sbit __at 0x82 P0_2;
+__sbit __at 0x83 P0_3;
+__sbit __at 0x84 P0_4;
+__sbit __at 0x85 P0_5;
+__sbit __at 0x86 P0_6;
+__sbit __at 0x87 P0_7;
+
+__sfr __at 0x90 P1;
+
+__sbit __at 0x90 P1_0;
+__sbit __at 0x91 P1_1;
+__sbit __at 0x92 P1_2;
+__sbit __at 0x93 P1_3;
+__sbit __at 0x94 P1_4;
+__sbit __at 0x95 P1_5;
+__sbit __at 0x96 P1_6;
+__sbit __at 0x97 P1_7;
+
+__sfr __at 0xa0 P2;
+
+__sbit __at 0xa0 P2_0;
+__sbit __at 0xa1 P2_1;
+__sbit __at 0xa2 P2_2;
+__sbit __at 0xa3 P2_3;
+__sbit __at 0xa4 P2_4;
+
+/* DMA controller */
+struct cc_dma_channel {
+       uint8_t src_high;
+       uint8_t src_low;
+       uint8_t dst_high;
+       uint8_t dst_low;
+       uint8_t len_high;
+       uint8_t len_low;
+       uint8_t cfg0;
+       uint8_t cfg1;
+};
+
+# define DMA_LEN_HIGH_VLEN_MASK                (7 << 5)
+# define DMA_LEN_HIGH_VLEN_LEN         (0 << 5)
+# define DMA_LEN_HIGH_VLEN_PLUS_1      (1 << 5)
+# define DMA_LEN_HIGH_VLEN             (2 << 5)
+# define DMA_LEN_HIGH_VLEN_PLUS_2      (3 << 5)
+# define DMA_LEN_HIGH_VLEN_PLUS_3      (4 << 5)
+# define DMA_LEN_HIGH_MASK             (0x1f)
+
+# define DMA_CFG0_WORDSIZE_8           (0 << 7)
+# define DMA_CFG0_WORDSIZE_16          (1 << 7)
+# define DMA_CFG0_TMODE_MASK           (3 << 5)
+# define DMA_CFG0_TMODE_SINGLE         (0 << 5)
+# define DMA_CFG0_TMODE_BLOCK          (1 << 5)
+# define DMA_CFG0_TMODE_REPEATED_SINGLE        (2 << 5)
+# define DMA_CFG0_TMODE_REPEATED_BLOCK (3 << 5)
+
+/*
+ * DMA triggers
+ */
+# define DMA_CFG0_TRIGGER_NONE         0
+# define DMA_CFG0_TRIGGER_PREV         1
+# define DMA_CFG0_TRIGGER_T1_CH0       2
+# define DMA_CFG0_TRIGGER_T1_CH1       3
+# define DMA_CFG0_TRIGGER_T1_CH2       4
+# define DMA_CFG0_TRIGGER_T2_OVFL      6
+# define DMA_CFG0_TRIGGER_T3_CH0       7
+# define DMA_CFG0_TRIGGER_T3_CH1       8
+# define DMA_CFG0_TRIGGER_T4_CH0       9
+# define DMA_CFG0_TRIGGER_T4_CH1       10
+# define DMA_CFG0_TRIGGER_IOC_0                12
+# define DMA_CFG0_TRIGGER_IOC_1                13
+# define DMA_CFG0_TRIGGER_URX0         14
+# define DMA_CFG0_TRIGGER_UTX0         15
+# define DMA_CFG0_TRIGGER_URX1         16
+# define DMA_CFG0_TRIGGER_UTX1         17
+# define DMA_CFG0_TRIGGER_FLASH                18
+# define DMA_CFG0_TRIGGER_RADIO                19
+# define DMA_CFG0_TRIGGER_ADC_CHALL    20
+# define DMA_CFG0_TRIGGER_ADC_CH0      21
+# define DMA_CFG0_TRIGGER_ADC_CH1      22
+# define DMA_CFG0_TRIGGER_ADC_CH2      23
+# define DMA_CFG0_TRIGGER_ADC_CH3      24
+# define DMA_CFG0_TRIGGER_ADC_CH4      25
+# define DMA_CFG0_TRIGGER_ADC_CH5      26
+# define DMA_CFG0_TRIGGER_ADC_CH6      27
+# define DMA_CFG0_TRIGGER_I2SRX                27
+# define DMA_CFG0_TRIGGER_ADC_CH7      28
+# define DMA_CFG0_TRIGGER_I2STX                28
+# define DMA_CFG0_TRIGGER_ENC_DW       29
+# define DMA_CFG0_TRIGGER_ENC_UP       30
+
+# define DMA_CFG1_SRCINC_MASK          (3 << 6)
+# define DMA_CFG1_SRCINC_0             (0 << 6)
+# define DMA_CFG1_SRCINC_1             (1 << 6)
+# define DMA_CFG1_SRCINC_2             (2 << 6)
+# define DMA_CFG1_SRCINC_MINUS_1       (3 << 6)
+
+# define DMA_CFG1_DESTINC_MASK         (3 << 4)
+# define DMA_CFG1_DESTINC_0            (0 << 4)
+# define DMA_CFG1_DESTINC_1            (1 << 4)
+# define DMA_CFG1_DESTINC_2            (2 << 4)
+# define DMA_CFG1_DESTINC_MINUS_1      (3 << 4)
+
+# define DMA_CFG1_IRQMASK              (1 << 3)
+# define DMA_CFG1_M8                   (1 << 2)
+
+# define DMA_CFG1_PRIORITY_MASK                (3 << 0)
+# define DMA_CFG1_PRIORITY_LOW         (0 << 0)
+# define DMA_CFG1_PRIORITY_NORMAL      (1 << 0)
+# define DMA_CFG1_PRIORITY_HIGH                (2 << 0)
+
+/*
+ * DMAARM - DMA Channel Arm
+ */
+
+__sfr __at 0xD6 DMAARM;
+
+# define DMAARM_ABORT                  (1 << 7)
+# define DMAARM_DMAARM4                        (1 << 4)
+# define DMAARM_DMAARM3                        (1 << 3)
+# define DMAARM_DMAARM2                        (1 << 2)
+# define DMAARM_DMAARM1                        (1 << 1)
+# define DMAARM_DMAARM0                        (1 << 0)
+
+/*
+ * DMAREQ - DMA Channel Start Request and Status
+ */
+
+__sfr __at 0xD7 DMAREQ;
+
+# define DMAREQ_DMAREQ4                        (1 << 4)
+# define DMAREQ_DMAREQ3                        (1 << 3)
+# define DMAREQ_DMAREQ2                        (1 << 2)
+# define DMAREQ_DMAREQ1                        (1 << 1)
+# define DMAREQ_DMAREQ0                        (1 << 0)
+
+/*
+ * DMA configuration 0 address
+ */
+
+__sfr __at 0xD5 DMA0CFGH;
+__sfr __at 0xD4 DMA0CFGL;
+
+/*
+ * DMA configuration 1-4 address
+ */
+
+__sfr __at 0xD3 DMA1CFGH;
+__sfr __at 0xD2 DMA1CFGL;
+
+/*
+ * DMAIRQ - DMA Interrupt Flag
+ */
+
+__sfr __at 0xD1 DMAIRQ;
+
+# define DMAIRQ_DMAIF4                 (1 << 4)
+# define DMAIRQ_DMAIF3                 (1 << 3)
+# define DMAIRQ_DMAIF2                 (1 << 2)
+# define DMAIRQ_DMAIF1                 (1 << 1)
+# define DMAIRQ_DMAIF0                 (1 << 0)
+
+/*
+ * UART registers
+ */
+
+/* USART config/status registers */
+__sfr __at 0x86 U0CSR;
+__sfr __at 0xF8 U1CSR;
+
+# define UxCSR_MODE_UART               (1 << 7)
+# define UxCSR_MODE_SPI                        (0 << 7)
+# define UxCSR_RE                      (1 << 6)
+# define UxCSR_SLAVE                   (1 << 5)
+# define UxCSR_MASTER                  (0 << 5)
+# define UxCSR_FE                      (1 << 4)
+# define UxCSR_ERR                     (1 << 3)
+# define UxCSR_RX_BYTE                 (1 << 2)
+# define UxCSR_TX_BYTE                 (1 << 1)
+# define UxCSR_ACTIVE                  (1 << 0)
+
+/* UART configuration registers */
+__sfr __at 0xc4 U0UCR;
+__sfr __at 0xfb U1UCR;
+
+# define UxUCR_FLUSH                    (1 << 7)
+# define UxUCR_FLOW_DISABLE             (0 << 6)
+# define UxUCR_FLOW_ENABLE              (1 << 6)
+# define UxUCR_D9_EVEN_PARITY           (0 << 5)
+# define UxUCR_D9_ODD_PARITY            (1 << 5)
+# define UxUCR_BIT9_8_BITS              (0 << 4)
+# define UxUCR_BIT9_9_BITS              (1 << 4)
+# define UxUCR_PARITY_DISABLE           (0 << 3)
+# define UxUCR_PARITY_ENABLE            (1 << 3)
+# define UxUCR_SPB_1_STOP_BIT           (0 << 2)
+# define UxUCR_SPB_2_STOP_BITS          (1 << 2)
+# define UxUCR_STOP_LOW                 (0 << 1)
+# define UxUCR_STOP_HIGH                (1 << 1)
+# define UxUCR_START_LOW                (0 << 0)
+# define UxUCR_START_HIGH               (1 << 0)
+
+/* USART General configuration registers (mostly SPI) */
+__sfr __at 0xc5 U0GCR;
+__sfr __at 0xfc U1GCR;
+
+# define UxGCR_CPOL_NEGATIVE           (0 << 7)
+# define UxGCR_CPOL_POSITIVE           (1 << 7)
+# define UxGCR_CPHA_FIRST_EDGE         (0 << 6)
+# define UxGCR_CPHA_SECOND_EDGE                (1 << 6)
+# define UxGCR_ORDER_LSB               (0 << 5)
+# define UxGCR_ORDER_MSB               (1 << 5)
+# define UxGCR_BAUD_E_MASK             (0x1f)
+# define UxGCR_BAUD_E_SHIFT            0
+
+/* USART data registers */
+__sfr __at 0xc1 U0DBUF;
+__xdata __at (0xDFC1) volatile uint8_t U0DBUFXADDR;
+__sfr __at 0xf9 U1DBUF;
+__xdata __at (0xDFF9) volatile uint8_t U1DBUFXADDR;
+
+/* USART baud rate registers, M value */
+__sfr __at 0xc2 U0BAUD;
+__sfr __at 0xfa U1BAUD;
+
+/* Flash controller */
+
+__sfr __at 0xAE FCTL;
+#define FCTL_BUSY              (1 << 7)
+#define FCTL_SWBSY             (1 << 6)
+#define FCTL_CONTRD_ENABLE     (1 << 4)
+#define FCTL_WRITE             (1 << 1)
+#define FCTL_ERASE             (1 << 0)
+
+/* Flash write data. Write two bytes here */
+__sfr __at 0xAF FWDATA;
+__xdata __at (0xDFAF) volatile uint8_t FWDATAXADDR;
+
+/* Flash write/erase address */
+__sfr __at 0xAD FADDRH;
+__sfr __at 0xAC FADDRL;
+
+/* Flash timing */
+__sfr __at 0xAB FWT;
+
+/* Radio */
+
+__sfr __at 0xD9 RFD;
+__xdata __at (0xDFD9) volatile uint8_t RFDXADDR;
+
+__sfr __at 0xE9 RFIF;
+#define RFIF_IM_TXUNF  (1 << 7)
+#define RFIF_IM_RXOVF  (1 << 6)
+#define RFIF_IM_TIMEOUT        (1 << 5)
+#define RFIF_IM_DONE   (1 << 4)
+#define RFIF_IM_CS     (1 << 3)
+#define RFIF_IM_PQT    (1 << 2)
+#define RFIF_IM_CCA    (1 << 1)
+#define RFIF_IM_SFD    (1 << 0)
+
+__sfr __at 0x91 RFIM;
+#define RFIM_IM_TXUNF  (1 << 7)
+#define RFIM_IM_RXOVF  (1 << 6)
+#define RFIM_IM_TIMEOUT        (1 << 5)
+#define RFIM_IM_DONE   (1 << 4)
+#define RFIM_IM_CS     (1 << 3)
+#define RFIM_IM_PQT    (1 << 2)
+#define RFIM_IM_CCA    (1 << 1)
+#define RFIM_IM_SFD    (1 << 0)
+
+__sfr __at 0xE1 RFST;
+
+#define RFST_SFSTXON   0x00
+#define RFST_SCAL      0x01
+#define RFST_SRX       0x02
+#define RFST_STX       0x03
+#define RFST_SIDLE     0x04
+
+__xdata __at (0xdf00) uint8_t RF[0x3c];
+
+__xdata __at (0xdf2f) uint8_t RF_IOCFG2;
+#define RF_IOCFG2_OFF  0x2f
+
+__xdata __at (0xdf30) uint8_t RF_IOCFG1;
+#define RF_IOCFG1_OFF  0x30
+
+__xdata __at (0xdf31) uint8_t RF_IOCFG0;
+#define RF_IOCFG0_OFF  0x31
+
+__xdata __at (0xdf00) uint8_t RF_SYNC1;
+#define RF_SYNC1_OFF   0x00
+
+__xdata __at (0xdf01) uint8_t RF_SYNC0;
+#define RF_SYNC0_OFF   0x01
+
+__xdata __at (0xdf02) uint8_t RF_PKTLEN;
+#define RF_PKTLEN_OFF  0x02
+
+__xdata __at (0xdf03) uint8_t RF_PKTCTRL1;
+#define RF_PKTCTRL1_OFF        0x03
+#define PKTCTRL1_PQT_MASK                      (0x7 << 5)
+#define PKTCTRL1_PQT_SHIFT                     5
+#define PKTCTRL1_APPEND_STATUS                 (1 << 2)
+#define PKTCTRL1_ADR_CHK_NONE                  (0 << 0)
+#define PKTCTRL1_ADR_CHK_NO_BROADCAST          (1 << 0)
+#define PKTCTRL1_ADR_CHK_00_BROADCAST          (2 << 0)
+#define PKTCTRL1_ADR_CHK_00_FF_BROADCAST       (3 << 0)
+
+/* If APPEND_STATUS is used, two bytes will be added to the packet data */
+#define PKT_APPEND_STATUS_0_RSSI_MASK          (0xff)
+#define PKT_APPEND_STATUS_0_RSSI_SHIFT         0
+#define PKT_APPEND_STATUS_1_CRC_OK             (1 << 7)
+#define PKT_APPEND_STATUS_1_LQI_MASK           (0x7f)
+#define PKT_APPEND_STATUS_1_LQI_SHIFT          0
+
+__xdata __at (0xdf04) uint8_t RF_PKTCTRL0;
+#define RF_PKTCTRL0_OFF        0x04
+#define RF_PKTCTRL0_WHITE_DATA                 (1 << 6)
+#define RF_PKTCTRL0_PKT_FORMAT_NORMAL          (0 << 4)
+#define RF_PKTCTRL0_PKT_FORMAT_RANDOM          (2 << 4)
+#define RF_PKTCTRL0_CRC_EN                     (1 << 2)
+#define RF_PKTCTRL0_LENGTH_CONFIG_FIXED                (0 << 0)
+#define RF_PKTCTRL0_LENGTH_CONFIG_VARIABLE     (1 << 0)
+
+__xdata __at (0xdf05) uint8_t RF_ADDR;
+#define RF_ADDR_OFF    0x05
+
+__xdata __at (0xdf06) uint8_t RF_CHANNR;
+#define RF_CHANNR_OFF  0x06
+
+__xdata __at (0xdf07) uint8_t RF_FSCTRL1;
+#define RF_FSCTRL1_OFF 0x07
+
+#define RF_FSCTRL1_FREQ_IF_SHIFT       (0)
+
+__xdata __at (0xdf08) uint8_t RF_FSCTRL0;
+#define RF_FSCTRL0_OFF 0x08
+
+#define RF_FSCTRL0_FREQOFF_SHIFT       (0)
+
+__xdata __at (0xdf09) uint8_t RF_FREQ2;
+#define RF_FREQ2_OFF   0x09
+
+__xdata __at (0xdf0a) uint8_t RF_FREQ1;
+#define RF_FREQ1_OFF   0x0a
+
+__xdata __at (0xdf0b) uint8_t RF_FREQ0;
+#define RF_FREQ0_OFF   0x0b
+
+__xdata __at (0xdf0c) uint8_t RF_MDMCFG4;
+#define RF_MDMCFG4_OFF 0x0c
+
+#define RF_MDMCFG4_CHANBW_E_SHIFT      6
+#define RF_MDMCFG4_CHANBW_M_SHIFT      4
+#define RF_MDMCFG4_DRATE_E_SHIFT       0
+
+__xdata __at (0xdf0d) uint8_t RF_MDMCFG3;
+#define RF_MDMCFG3_OFF 0x0d
+
+#define RF_MDMCFG3_DRATE_M_SHIFT       0
+
+__xdata __at (0xdf0e) uint8_t RF_MDMCFG2;
+#define RF_MDMCFG2_OFF 0x0e
+
+#define RF_MDMCFG2_DEM_DCFILT_OFF      (1 << 7)
+#define RF_MDMCFG2_DEM_DCFILT_ON       (0 << 7)
+
+#define RF_MDMCFG2_MOD_FORMAT_MASK     (7 << 4)
+#define RF_MDMCFG2_MOD_FORMAT_2_FSK    (0 << 4)
+#define RF_MDMCFG2_MOD_FORMAT_GFSK     (1 << 4)
+#define RF_MDMCFG2_MOD_FORMAT_ASK_OOK  (3 << 4)
+#define RF_MDMCFG2_MOD_FORMAT_MSK      (7 << 4)
+
+#define RF_MDMCFG2_MANCHESTER_EN       (1 << 3)
+
+#define RF_MDMCFG2_SYNC_MODE_MASK              (0x7 << 0)
+#define RF_MDMCFG2_SYNC_MODE_NONE              (0x0 << 0)
+#define RF_MDMCFG2_SYNC_MODE_15_16             (0x1 << 0)
+#define RF_MDMCFG2_SYNC_MODE_16_16             (0x2 << 0)
+#define RF_MDMCFG2_SYNC_MODE_30_32             (0x3 << 0)
+#define RF_MDMCFG2_SYNC_MODE_NONE_THRES                (0x4 << 0)
+#define RF_MDMCFG2_SYNC_MODE_15_16_THRES       (0x5 << 0)
+#define RF_MDMCFG2_SYNC_MODE_16_16_THRES       (0x6 << 0)
+#define RF_MDMCFG2_SYNC_MODE_30_32_THRES       (0x7 << 0)
+
+__xdata __at (0xdf0f) uint8_t RF_MDMCFG1;
+#define RF_MDMCFG1_OFF 0x0f
+
+#define RF_MDMCFG1_FEC_EN                      (1 << 7)
+#define RF_MDMCFG1_FEC_DIS                     (0 << 7)
+
+#define RF_MDMCFG1_NUM_PREAMBLE_MASK           (7 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_2              (0 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_3              (1 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_4              (2 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_6              (3 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_8              (4 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_12             (5 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_16             (6 << 4)
+#define RF_MDMCFG1_NUM_PREAMBLE_24             (7 << 4)
+
+#define RF_MDMCFG1_CHANSPC_E_MASK              (3 << 0)
+#define RF_MDMCFG1_CHANSPC_E_SHIFT             (0)
+
+__xdata __at (0xdf10) uint8_t RF_MDMCFG0;
+#define RF_MDMCFG0_OFF 0x10
+
+#define RF_MDMCFG0_CHANSPC_M_SHIFT             (0)
+
+__xdata __at (0xdf11) uint8_t RF_DEVIATN;
+#define RF_DEVIATN_OFF 0x11
+
+#define RF_DEVIATN_DEVIATION_E_SHIFT           4
+#define RF_DEVIATN_DEVIATION_M_SHIFT           0
+
+__xdata __at (0xdf12) uint8_t RF_MCSM2;
+#define RF_MCSM2_OFF   0x12
+#define RF_MCSM2_RX_TIME_RSSI                  (1 << 4)
+#define RF_MCSM2_RX_TIME_QUAL                  (1 << 3)
+#define RF_MCSM2_RX_TIME_MASK                  (0x7)
+#define RF_MCSM2_RX_TIME_SHIFT                 0
+#define RF_MCSM2_RX_TIME_END_OF_PACKET         (7)
+
+__xdata __at (0xdf13) uint8_t RF_MCSM1;
+#define RF_MCSM1_OFF   0x13
+#define RF_MCSM1_CCA_MODE_ALWAYS                       (0 << 4)
+#define RF_MCSM1_CCA_MODE_RSSI_BELOW                   (1 << 4)
+#define RF_MCSM1_CCA_MODE_UNLESS_RECEIVING             (2 << 4)
+#define RF_MCSM1_CCA_MODE_RSSI_BELOW_UNLESS_RECEIVING  (3 << 4)
+#define RF_MCSM1_RXOFF_MODE_IDLE                       (0 << 2)
+#define RF_MCSM1_RXOFF_MODE_FSTXON                     (1 << 2)
+#define RF_MCSM1_RXOFF_MODE_TX                         (2 << 2)
+#define RF_MCSM1_RXOFF_MODE_RX                         (3 << 2)
+#define RF_MCSM1_TXOFF_MODE_IDLE                       (0 << 0)
+#define RF_MCSM1_TXOFF_MODE_FSTXON                     (1 << 0)
+#define RF_MCSM1_TXOFF_MODE_TX                         (2 << 0)
+#define RF_MCSM1_TXOFF_MODE_RX                         (3 << 0)
+
+__xdata __at (0xdf14) uint8_t RF_MCSM0;
+#define RF_MCSM0_OFF   0x14
+#define RF_MCSM0_FS_AUTOCAL_NEVER              (0 << 4)
+#define RF_MCSM0_FS_AUTOCAL_FROM_IDLE          (1 << 4)
+#define RF_MCSM0_FS_AUTOCAL_TO_IDLE            (2 << 4)
+#define RF_MCSM0_FS_AUTOCAL_TO_IDLE_EVERY_4    (3 << 4)
+#define RF_MCSM0_MAGIC_3                       (1 << 3)
+#define RF_MCSM0_MAGIC_2                       (1 << 2)
+#define RF_MCSM0_CLOSE_IN_RX_0DB               (0 << 0)
+#define RF_MCSM0_CLOSE_IN_RX_6DB               (1 << 0)
+#define RF_MCSM0_CLOSE_IN_RX_12DB              (2 << 0)
+#define RF_MCSM0_CLOSE_IN_RX_18DB              (3 << 0)
+
+__xdata __at (0xdf15) uint8_t RF_FOCCFG;
+#define RF_FOCCFG_OFF  0x15
+#define RF_FOCCFG_FOC_BS_CS_GATE               (1 << 5)
+#define RF_FOCCFG_FOC_PRE_K_1K                 (0 << 3)
+#define RF_FOCCFG_FOC_PRE_K_2K                 (1 << 3)
+#define RF_FOCCFG_FOC_PRE_K_3K                 (2 << 3)
+#define RF_FOCCFG_FOC_PRE_K_4K                 (3 << 3)
+#define RF_FOCCFG_FOC_POST_K_PRE_K             (0 << 2)
+#define RF_FOCCFG_FOC_POST_K_PRE_K_OVER_2      (1 << 2)
+#define RF_FOCCFG_FOC_LIMIT_0                  (0 << 0)
+#define RF_FOCCFG_FOC_LIMIT_BW_OVER_8          (1 << 0)
+#define RF_FOCCFG_FOC_LIMIT_BW_OVER_4          (2 << 0)
+#define RF_FOCCFG_FOC_LIMIT_BW_OVER_2          (3 << 0)
+
+__xdata __at (0xdf16) uint8_t RF_BSCFG;
+#define RF_BSCFG_OFF   0x16
+#define RF_BSCFG_BS_PRE_K_1K                   (0 << 6)
+#define RF_BSCFG_BS_PRE_K_2K                   (1 << 6)
+#define RF_BSCFG_BS_PRE_K_3K                   (2 << 6)
+#define RF_BSCFG_BS_PRE_K_4K                   (3 << 6)
+#define RF_BSCFG_BS_PRE_KP_1KP                 (0 << 4)
+#define RF_BSCFG_BS_PRE_KP_2KP                 (1 << 4)
+#define RF_BSCFG_BS_PRE_KP_3KP                 (2 << 4)
+#define RF_BSCFG_BS_PRE_KP_4KP                 (3 << 4)
+#define RF_BSCFG_BS_POST_KI_PRE_KI             (0 << 3)
+#define RF_BSCFG_BS_POST_KI_PRE_KI_OVER_2      (1 << 3)
+#define RF_BSCFG_BS_POST_KP_PRE_KP             (0 << 2)
+#define RF_BSCFG_BS_POST_KP_PRE_KP_OVER_2      (1 << 2)
+#define RF_BSCFG_BS_LIMIT_0                    (0 << 0)
+#define RF_BSCFG_BS_LIMIT_3_125                        (1 << 0)
+#define RF_BSCFG_BS_LIMIT_6_25                 (2 << 0)
+#define RF_BSCFG_BS_LIMIT_12_5                 (3 << 0)
+
+__xdata __at (0xdf17) uint8_t RF_AGCCTRL2;
+#define RF_AGCCTRL2_OFF        0x17
+
+__xdata __at (0xdf18) uint8_t RF_AGCCTRL1;
+#define RF_AGCCTRL1_OFF        0x18
+
+__xdata __at (0xdf19) uint8_t RF_AGCCTRL0;
+#define RF_AGCCTRL0_OFF        0x19
+
+__xdata __at (0xdf1a) uint8_t RF_FREND1;
+#define RF_FREND1_OFF  0x1a
+
+#define RF_FREND1_LNA_CURRENT_SHIFT            6
+#define RF_FREND1_LNA2MIX_CURRENT_SHIFT                4
+#define RF_FREND1_LODIV_BUF_CURRENT_RX_SHIFT   2
+#define RF_FREND1_MIX_CURRENT_SHIFT            0
+
+__xdata __at (0xdf1b) uint8_t RF_FREND0;
+#define RF_FREND0_OFF  0x1b
+
+#define RF_FREND0_LODIV_BUF_CURRENT_TX_MASK    (0x3 << 4)
+#define RF_FREND0_LODIV_BUF_CURRENT_TX_SHIFT   4
+#define RF_FREND0_PA_POWER_MASK                        (0x7)
+#define RF_FREND0_PA_POWER_SHIFT               0
+
+__xdata __at (0xdf1c) uint8_t RF_FSCAL3;
+#define RF_FSCAL3_OFF  0x1c
+
+__xdata __at (0xdf1d) uint8_t RF_FSCAL2;
+#define RF_FSCAL2_OFF  0x1d
+
+__xdata __at (0xdf1e) uint8_t RF_FSCAL1;
+#define RF_FSCAL1_OFF  0x1e
+
+__xdata __at (0xdf1f) uint8_t RF_FSCAL0;
+#define RF_FSCAL0_OFF  0x1f
+
+__xdata __at (0xdf23) uint8_t RF_TEST2;
+#define RF_TEST2_OFF   0x23
+
+#define RF_TEST2_NORMAL_MAGIC          0x88
+#define RF_TEST2_RX_LOW_DATA_RATE_MAGIC        0x81
+
+__xdata __at (0xdf24) uint8_t RF_TEST1;
+#define RF_TEST1_OFF   0x24
+
+#define RF_TEST1_TX_MAGIC              0x31
+#define RF_TEST1_RX_LOW_DATA_RATE_MAGIC        0x35
+
+__xdata __at (0xdf25) uint8_t RF_TEST0;
+#define RF_TEST0_OFF   0x25
+
+#define RF_TEST0_7_2_MASK              (0xfc)
+#define RF_TEST0_VCO_SEL_CAL_EN                (1 << 1)
+#define RF_TEST0_0_MASK                        (1)
+
+/* These are undocumented, and must be computed
+ * using the provided tool.
+ */
+__xdata __at (0xdf27) uint8_t RF_PA_TABLE7;
+#define RF_PA_TABLE7_OFF       0x27
+
+__xdata __at (0xdf28) uint8_t RF_PA_TABLE6;
+#define RF_PA_TABLE6_OFF       0x28
+
+__xdata __at (0xdf29) uint8_t RF_PA_TABLE5;
+#define RF_PA_TABLE5_OFF       0x29
+
+__xdata __at (0xdf2a) uint8_t RF_PA_TABLE4;
+#define RF_PA_TABLE4_OFF       0x2a
+
+__xdata __at (0xdf2b) uint8_t RF_PA_TABLE3;
+#define RF_PA_TABLE3_OFF       0x2b
+
+__xdata __at (0xdf2c) uint8_t RF_PA_TABLE2;
+#define RF_PA_TABLE2_OFF       0x2c
+
+__xdata __at (0xdf2d) uint8_t RF_PA_TABLE1;
+#define RF_PA_TABLE1_OFF       0x2d
+
+__xdata __at (0xdf2e) uint8_t RF_PA_TABLE0;
+#define RF_PA_TABLE0_OFF       0x2e
+
+__xdata __at (0xdf36) uint8_t RF_PARTNUM;
+#define RF_PARTNUM_OFF 0x36
+
+__xdata __at (0xdf37) uint8_t RF_VERSION;
+#define RF_VERSION_OFF 0x37
+
+__xdata __at (0xdf38) uint8_t RF_FREQEST;
+#define RF_FREQEST_OFF 0x38
+
+__xdata __at (0xdf39) uint8_t RF_LQI;
+#define RF_LQI_OFF     0x39
+
+#define RF_LQI_CRC_OK                  (1 << 7)
+#define RF_LQI_LQI_EST_MASK            (0x7f)
+
+__xdata __at (0xdf3a) uint8_t RF_RSSI;
+#define RF_RSSI_OFF    0x3a
+
+__xdata __at (0xdf3b) uint8_t RF_MARCSTATE;
+#define RF_MARCSTATE_OFF       0x3b
+
+#define RF_MARCSTATE_MASK              0x1f
+#define RF_MARCSTATE_SLEEP             0x00
+#define RF_MARCSTATE_IDLE              0x01
+#define RF_MARCSTATE_VCOON_MC          0x03
+#define RF_MARCSTATE_REGON_MC          0x04
+#define RF_MARCSTATE_MANCAL            0x05
+#define RF_MARCSTATE_VCOON             0x06
+#define RF_MARCSTATE_REGON             0x07
+#define RF_MARCSTATE_STARTCAL          0x08
+#define RF_MARCSTATE_BWBOOST           0x09
+#define RF_MARCSTATE_FS_LOCK           0x0a
+#define RF_MARCSTATE_IFADCON           0x0b
+#define RF_MARCSTATE_ENDCAL            0x0c
+#define RF_MARCSTATE_RX                        0x0d
+#define RF_MARCSTATE_RX_END            0x0e
+#define RF_MARCSTATE_RX_RST            0x0f
+#define RF_MARCSTATE_TXRX_SWITCH       0x10
+#define RF_MARCSTATE_RX_OVERFLOW       0x11
+#define RF_MARCSTATE_FSTXON            0x12
+#define RF_MARCSTATE_TX                        0x13
+#define RF_MARCSTATE_TX_END            0x14
+#define RF_MARCSTATE_RXTX_SWITCH       0x15
+#define RF_MARCSTATE_TX_UNDERFLOW      0x16
+
+
+__xdata __at (0xdf3c) uint8_t RF_PKTSTATUS;
+#define RF_PKTSTATUS_OFF       0x3c
+
+#define RF_PKTSTATUS_CRC_OK            (1 << 7)
+#define RF_PKTSTATUS_CS                        (1 << 6)
+#define RF_PKTSTATUS_PQT_REACHED       (1 << 5)
+#define RF_PKTSTATUS_CCA               (1 << 4)
+#define RF_PKTSTATUS_SFD               (1 << 3)
+
+__xdata __at (0xdf3d) uint8_t RF_VCO_VC_DAC;
+#define RF_VCO_VC_DAC_OFF      0x3d
+
+/* AES engine */
+
+__sfr __at 0xB1 ENCDI;
+__sfr __at 0xB2 ENCDO;
+__xdata __at (0xDFB1) volatile uint8_t ENCDIXADDR;
+__xdata __at (0xDFB2) volatile uint8_t ENCDOXADDR;
+
+__sfr __at 0xB3 ENCCCS;
+
+#define ENCCCS_MODE_CBC                (0 << 4)
+#define ENCCCS_MODE_CFB                (1 << 4)
+#define ENCCCS_MODE_OFB                (2 << 4)
+#define ENCCCS_MODE_CTR                (3 << 4)
+#define ENCCCS_MODE_ECB                (4 << 4)
+#define ENCCCS_MODE_CBC_MAC    (5 << 4)
+#define ENCCCS_RDY             (1 << 3)
+#define ENCCCS_CMD_ENCRYPT     (0 << 1)
+#define ENCCCS_CMD_DECRYPT     (1 << 1)
+#define ENCCCS_CMD_LOAD_KEY    (2 << 1)
+#define ENCCCS_CMD_LOAD_IV     (3 << 1)
+#define ENCCCS_START           (1 << 0)
+
+#endif
diff --git a/src/check-stack b/src/check-stack
deleted file mode 100755 (executable)
index 82680b8..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh
-HEADER=$1
-MEM=$2
-
-HEADER_STACK=`awk '/#define AO_STACK_START/ {print $3}' $HEADER | nickle`
-MEM_STACK=`awk '/Stack starts at/ {print $4}' $MEM | nickle`
-
-if [ "$HEADER_STACK" -lt "$MEM_STACK" ]; then
-       MIN=0x`nickle -e "$MEM_STACK # 16"`
-       echo "Set AO_STACK_START to at least $MIN"
-       exit 1
-else
-       exit 0
-fi
diff --git a/src/core/altitude.h b/src/core/altitude.h
new file mode 100644 (file)
index 0000000..a278bbc
--- /dev/null
@@ -0,0 +1,132 @@
+/*max error 3.197865153490684 at   0.782%. Average error 0.260150920474668*/
+#define NALT 129
+#define ALT_FRAC_BITS 8
+    15835, /*  10.56 kPa   0.000% */
+    15332, /*  11.42 kPa   0.781% */
+    14868, /*  12.29 kPa   1.563% */
+    14435, /*  13.16 kPa   2.344% */
+    14030, /*  14.02 kPa   3.125% */
+    13649, /*  14.90 kPa   3.906% */
+    13290, /*  15.76 kPa   4.688% */
+    12950, /*  16.63 kPa   5.469% */
+    12627, /*  17.50 kPa   6.250% */
+    12320, /*  18.37 kPa   7.031% */
+    12027, /*  19.24 kPa   7.813% */
+    11747, /*  20.10 kPa   8.594% */
+    11479, /*  20.97 kPa   9.375% */
+    11222, /*  21.84 kPa  10.156% */
+    10975, /*  22.71 kPa  10.938% */
+    10736, /*  23.58 kPa  11.719% */
+    10504, /*  24.44 kPa  12.500% */
+    10278, /*  25.31 kPa  13.281% */
+    10059, /*  26.18 kPa  14.063% */
+     9846, /*  27.05 kPa  14.844% */
+     9638, /*  27.91 kPa  15.625% */
+     9435, /*  28.78 kPa  16.406% */
+     9237, /*  29.65 kPa  17.188% */
+     9044, /*  30.52 kPa  17.969% */
+     8855, /*  31.39 kPa  18.750% */
+     8670, /*  32.26 kPa  19.531% */
+     8490, /*  33.13 kPa  20.313% */
+     8313, /*  33.99 kPa  21.094% */
+     8140, /*  34.86 kPa  21.875% */
+     7970, /*  35.73 kPa  22.656% */
+     7803, /*  36.60 kPa  23.438% */
+     7640, /*  37.47 kPa  24.219% */
+     7480, /*  38.33 kPa  25.000% */
+     7322, /*  39.20 kPa  25.781% */
+     7168, /*  40.07 kPa  26.563% */
+     7016, /*  40.94 kPa  27.344% */
+     6867, /*  41.80 kPa  28.125% */
+     6720, /*  42.67 kPa  28.906% */
+     6575, /*  43.54 kPa  29.688% */
+     6433, /*  44.41 kPa  30.469% */
+     6294, /*  45.28 kPa  31.250% */
+     6156, /*  46.15 kPa  32.031% */
+     6020, /*  47.01 kPa  32.813% */
+     5887, /*  47.88 kPa  33.594% */
+     5755, /*  48.75 kPa  34.375% */
+     5625, /*  49.62 kPa  35.156% */
+     5497, /*  50.49 kPa  35.938% */
+     5371, /*  51.35 kPa  36.719% */
+     5247, /*  52.22 kPa  37.500% */
+     5124, /*  53.09 kPa  38.281% */
+     5003, /*  53.96 kPa  39.063% */
+     4883, /*  54.83 kPa  39.844% */
+     4765, /*  55.69 kPa  40.625% */
+     4648, /*  56.56 kPa  41.406% */
+     4533, /*  57.43 kPa  42.188% */
+     4419, /*  58.30 kPa  42.969% */
+     4307, /*  59.17 kPa  43.750% */
+     4196, /*  60.03 kPa  44.531% */
+     4086, /*  60.90 kPa  45.313% */
+     3977, /*  61.77 kPa  46.094% */
+     3870, /*  62.63 kPa  46.875% */
+     3764, /*  63.51 kPa  47.656% */
+     3659, /*  64.38 kPa  48.438% */
+     3555, /*  65.24 kPa  49.219% */
+     3453, /*  66.11 kPa  50.000% */
+     3351, /*  66.98 kPa  50.781% */
+     3250, /*  67.85 kPa  51.563% */
+     3151, /*  68.72 kPa  52.344% */
+     3052, /*  69.58 kPa  53.125% */
+     2955, /*  70.45 kPa  53.906% */
+     2858, /*  71.32 kPa  54.688% */
+     2763, /*  72.19 kPa  55.469% */
+     2668, /*  73.06 kPa  56.250% */
+     2574, /*  73.92 kPa  57.031% */
+     2482, /*  74.79 kPa  57.813% */
+     2390, /*  75.66 kPa  58.594% */
+     2298, /*  76.52 kPa  59.375% */
+     2208, /*  77.40 kPa  60.156% */
+     2119, /*  78.26 kPa  60.938% */
+     2030, /*  79.13 kPa  61.719% */
+     1942, /*  80.00 kPa  62.500% */
+     1855, /*  80.87 kPa  63.281% */
+     1769, /*  81.74 kPa  64.063% */
+     1683, /*  82.60 kPa  64.844% */
+     1598, /*  83.47 kPa  65.625% */
+     1514, /*  84.34 kPa  66.406% */
+     1430, /*  85.21 kPa  67.188% */
+     1347, /*  86.08 kPa  67.969% */
+     1265, /*  86.94 kPa  68.750% */
+     1184, /*  87.81 kPa  69.531% */
+     1103, /*  88.68 kPa  70.313% */
+     1023, /*  89.55 kPa  71.094% */
+      943, /*  90.41 kPa  71.875% */
+      864, /*  91.28 kPa  72.656% */
+      786, /*  92.15 kPa  73.438% */
+      708, /*  93.02 kPa  74.219% */
+      631, /*  93.89 kPa  75.000% */
+      554, /*  94.76 kPa  75.781% */
+      478, /*  95.63 kPa  76.563% */
+      403, /*  96.49 kPa  77.344% */
+      328, /*  97.36 kPa  78.125% */
+      254, /*  98.23 kPa  78.906% */
+      180, /*  99.10 kPa  79.688% */
+      106, /*  99.97 kPa  80.469% */
+       34, /* 100.83 kPa  81.250% */
+      -39, /* 101.70 kPa  82.031% */
+     -111, /* 102.57 kPa  82.813% */
+     -182, /* 103.44 kPa  83.594% */
+     -253, /* 104.30 kPa  84.375% */
+     -323, /* 105.17 kPa  85.156% */
+     -393, /* 106.04 kPa  85.938% */
+     -462, /* 106.91 kPa  86.719% */
+     -531, /* 107.78 kPa  87.500% */
+     -600, /* 108.65 kPa  88.281% */
+     -668, /* 109.51 kPa  89.063% */
+     -736, /* 110.38 kPa  89.844% */
+     -803, /* 111.25 kPa  90.625% */
+     -870, /* 112.12 kPa  91.406% */
+     -936, /* 112.99 kPa  92.188% */
+    -1002, /* 113.85 kPa  92.969% */
+    -1068, /* 114.72 kPa  93.750% */
+    -1133, /* 115.59 kPa  94.531% */
+    -1198, /* 116.46 kPa  95.313% */
+    -1262, /* 117.33 kPa  96.094% */
+    -1326, /* 118.19 kPa  96.875% */
+    -1389, /* 119.06 kPa  97.656% */
+    -1453, /* 119.93 kPa  98.438% */
+    -1516, /* 120.80 kPa  99.219% */
+    -1578, /* 121.67 kPa 100.000% */
diff --git a/src/core/ao.h b/src/core/ao.h
new file mode 100644 (file)
index 0000000..31ec468
--- /dev/null
@@ -0,0 +1,992 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_H_
+#define _AO_H_
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <ao_pins.h>
+#include <ao_arch.h>
+
+#define TRUE 1
+#define FALSE 0
+
+/* Convert a __data pointer into an __xdata pointer */
+#ifndef DATA_TO_XDATA
+#define DATA_TO_XDATA(a)       (a)
+#endif
+#ifndef PDATA_TO_XDATA
+#define PDATA_TO_XDATA(a)      (a)
+#endif
+#ifndef CODE_TO_XDATA
+#define CODE_TO_XDATA(a)       (a)
+#endif
+
+/* An AltOS task */
+struct ao_task {
+       __xdata void *wchan;            /* current wait channel (NULL if running) */
+       uint16_t alarm;                 /* abort ao_sleep time */
+       ao_arch_task_members            /* any architecture-specific fields */
+       uint8_t task_id;                /* unique id */
+       __code char *name;              /* task name */
+       uint8_t stack[AO_STACK_SIZE];   /* saved stack */
+};
+
+extern __xdata struct ao_task *__data ao_cur_task;
+
+#define AO_NUM_TASKS           16      /* maximum number of tasks */
+#define AO_NO_TASK             0       /* no task id */
+
+/*
+ ao_task.c
+ */
+
+/* Suspend the current task until wchan is awoken.
+ * returns:
+ *  0 on normal wake
+ *  1 on alarm
+ */
+uint8_t
+ao_sleep(__xdata void *wchan);
+
+/* Wake all tasks sleeping on wchan */
+void
+ao_wakeup(__xdata void *wchan);
+
+/* set an alarm to go off in 'delay' ticks */
+void
+ao_alarm(uint16_t delay);
+
+/* Clear any pending alarm */
+void
+ao_clear_alarm(void);
+
+/* Yield the processor to another task */
+void
+ao_yield(void) ao_arch_naked_declare;
+
+/* Add a task to the run queue */
+void
+ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *name) __reentrant;
+
+/* Terminate the current task */
+void
+ao_exit(void);
+
+/* Dump task info to console */
+void
+ao_task_info(void);
+
+/* Start the scheduler. This will not return */
+void
+ao_start_scheduler(void);
+
+/*
+ * ao_panic.c
+ */
+
+#define AO_PANIC_NO_TASK       1       /* AO_NUM_TASKS is not large enough */
+#define AO_PANIC_DMA           2       /* Attempt to start DMA while active */
+#define AO_PANIC_MUTEX         3       /* Mis-using mutex API */
+#define AO_PANIC_EE            4       /* Mis-using eeprom API */
+#define AO_PANIC_LOG           5       /* Failing to read/write log data */
+#define AO_PANIC_CMD           6       /* Too many command sets registered */
+#define AO_PANIC_STDIO         7       /* Too many stdio handlers registered */
+#define AO_PANIC_REBOOT                8       /* Reboot failed */
+#define AO_PANIC_FLASH         9       /* Invalid flash part (or wrong blocksize) */
+#define AO_PANIC_USB           10      /* Trying to send USB packet while busy */
+#define AO_PANIC_BT            11      /* Communications with bluetooth device failed */
+#define AO_PANIC_STACK         12      /* Stack overflow */
+#define AO_PANIC_SPI           13      /* SPI communication failure */
+#define AO_PANIC_SELF_TEST_CC1120      0x40 | 1        /* Self test failure */
+#define AO_PANIC_SELF_TEST_HMC5883     0x40 | 2        /* Self test failure */
+#define AO_PANIC_SELF_TEST_MPU6000     0x40 | 3        /* Self test failure */
+#define AO_PANIC_SELF_TEST_MS5607      0x40 | 4        /* Self test failure */
+
+/* Stop the operating system, beeping and blinking the reason */
+void
+ao_panic(uint8_t reason);
+
+/*
+ * ao_timer.c
+ */
+
+#ifndef AO_TICK_TYPE
+#define AO_TICK_TYPE   uint16_t
+#define AO_TICK_SIGNED int16_t
+#endif
+
+extern volatile __data AO_TICK_TYPE ao_tick_count;
+
+/* Our timer runs at 100Hz */
+#define AO_HERTZ               100
+#define AO_MS_TO_TICKS(ms)     ((ms) / (1000 / AO_HERTZ))
+#define AO_SEC_TO_TICKS(s)     ((s) * AO_HERTZ)
+
+/* Returns the current time in ticks */
+uint16_t
+ao_time(void);
+
+/* Suspend the current task until ticks time has passed */
+void
+ao_delay(uint16_t ticks);
+
+/* Set the ADC interval */
+void
+ao_timer_set_adc_interval(uint8_t interval) __critical;
+
+/* Timer interrupt */
+void
+ao_timer_isr(void) ao_arch_interrupt(9);
+
+/* Initialize the timer */
+void
+ao_timer_init(void);
+
+/* Initialize the hardware clock. Must be called first */
+void
+ao_clock_init(void);
+
+/*
+ * ao_mutex.c
+ */
+
+void
+ao_mutex_get(__xdata uint8_t *ao_mutex) __reentrant;
+
+void
+ao_mutex_put(__xdata uint8_t *ao_mutex) __reentrant;
+
+/*
+ * ao_cmd.c
+ */
+
+enum ao_cmd_status {
+       ao_cmd_success = 0,
+       ao_cmd_lex_error = 1,
+       ao_cmd_syntax_error = 2,
+};
+
+extern __pdata uint16_t ao_cmd_lex_i;
+extern __pdata uint32_t ao_cmd_lex_u32;
+extern __pdata char    ao_cmd_lex_c;
+extern __pdata enum ao_cmd_status ao_cmd_status;
+
+void
+ao_cmd_lex(void);
+
+void
+ao_cmd_put8(uint8_t v);
+
+void
+ao_cmd_put16(uint16_t v);
+
+uint8_t
+ao_cmd_is_white(void);
+
+void
+ao_cmd_white(void);
+
+int8_t
+ao_cmd_hexchar(char c);
+
+void
+ao_cmd_hexbyte(void);
+
+void
+ao_cmd_hex(void);
+
+void
+ao_cmd_decimal(void);
+
+uint8_t
+ao_match_word(__code char *word);
+
+struct ao_cmds {
+       void            (*func)(void);
+       __code char     *help;
+};
+
+void
+ao_cmd_register(const __code struct ao_cmds *cmds);
+
+void
+ao_cmd_init(void);
+
+#if HAS_CMD_FILTER
+/*
+ * Provided by an external module to filter raw command lines
+ */
+uint8_t
+ao_cmd_filter(void);
+#endif
+
+/*
+ * Various drivers
+ */
+#if HAS_ADC
+#include <ao_adc.h>
+#endif
+
+#if HAS_BEEP
+#include <ao_beep.h>
+#endif
+
+#if LEDS_AVAILABLE
+#include <ao_led.h>
+#endif
+
+#if HAS_USB
+#include <ao_usb.h>
+#endif
+
+#if HAS_EEPROM
+#include <ao_storage.h>
+#endif
+
+#if HAS_LOG
+#include <ao_log.h>
+#endif
+
+#if HAS_FLIGHT
+#include <ao_flight.h>
+#include <ao_sample.h>
+#endif
+
+/*
+ * ao_report.c
+ */
+
+#define AO_RDF_INTERVAL_TICKS  AO_SEC_TO_TICKS(5)
+#define AO_RDF_LENGTH_MS       500
+#define AO_RDF_CONTINUITY_MS   32
+#define AO_RDF_CONTINUITY_PAUSE        96
+#define AO_RDF_CONTINUITY_TOTAL        ((AO_RDF_CONTINUITY_PAUSE + AO_RDF_CONTINUITY_MS) * 3 + AO_RDF_CONTINUITY_PAUSE)
+
+/* This assumes that we're generating a 1kHz tone, which
+ * modulates the carrier at 2kbps, or 250kBps
+ */
+#define AO_MS_TO_RDF_LEN(ms) ((ms) / 4)
+
+#define AO_RADIO_RDF_LEN       AO_MS_TO_RDF_LEN(AO_RDF_LENGTH_MS)
+#define AO_RADIO_CONT_TONE_LEN AO_MS_TO_RDF_LEN(AO_RDF_CONTINUITY_MS)
+#define AO_RADIO_CONT_PAUSE_LEN        AO_MS_TO_RDF_LEN(AO_RDF_CONTINUITY_PAUSE)
+#define AO_RADIO_CONT_TOTAL_LEN        AO_MS_TO_RDF_LEN(AO_RDF_CONTINUITY_TOTAL)
+
+/* returns a value 0-3 to indicate igniter continuity */
+uint8_t
+ao_report_igniter(void);
+
+void
+ao_report_init(void);
+
+/*
+ * ao_convert.c
+ *
+ * Given raw data, convert to SI units
+ */
+
+/* pressure from the sensor to altitude in meters */
+int16_t
+ao_pres_to_altitude(int16_t pres) __reentrant;
+
+int16_t
+ao_altitude_to_pres(int16_t alt) __reentrant;
+
+int16_t
+ao_temp_to_dC(int16_t temp) __reentrant;
+
+/*
+ * ao_convert_pa.c
+ *
+ * Convert between pressure in Pa and altitude in meters
+ */
+
+int32_t
+ao_pa_to_altitude(int32_t pa);
+
+int32_t
+ao_altitude_to_pa(int32_t alt);
+
+#if HAS_DBG
+#include <ao_dbg.h>
+#endif
+
+#if HAS_SERIAL_0 || HAS_SERIAL_1 || HAS_SERIAL_2 || HAS_SERIAL_3
+#include <ao_serial.h>
+#endif
+
+
+/*
+ * ao_spi_slave.c
+ */
+
+uint8_t
+ao_spi_slave_recv(uint8_t *buf, uint8_t len);
+
+void
+ao_spi_slave_send(uint8_t *buf, uint8_t len);
+
+void
+ao_spi_slave_init(void);
+
+/* This must be defined by the product; it will get called when chip
+ * select goes low, at which point it should use ao_spi_read and
+ * ao_spi_write to deal with the request
+ */
+
+void
+ao_spi_slave(void);
+
+#include <ao_telemetry.h>
+/*
+ * ao_gps.c
+ */
+
+#define AO_GPS_NUM_SAT_MASK    (0xf << 0)
+#define AO_GPS_NUM_SAT_SHIFT   (0)
+
+#define AO_GPS_VALID           (1 << 4)
+#define AO_GPS_RUNNING         (1 << 5)
+#define AO_GPS_DATE_VALID      (1 << 6)
+#define AO_GPS_COURSE_VALID    (1 << 7)
+
+extern __pdata uint16_t ao_gps_tick;
+extern __xdata uint8_t ao_gps_mutex;
+extern __xdata struct ao_telemetry_location ao_gps_data;
+extern __xdata struct ao_telemetry_satellite ao_gps_tracking_data;
+
+struct ao_gps_orig {
+       uint8_t                 year;
+       uint8_t                 month;
+       uint8_t                 day;
+       uint8_t                 hour;
+       uint8_t                 minute;
+       uint8_t                 second;
+       uint8_t                 flags;
+       int32_t                 latitude;       /* degrees * 10⁷ */
+       int32_t                 longitude;      /* degrees * 10⁷ */
+       int16_t                 altitude;       /* m */
+       uint16_t                ground_speed;   /* cm/s */
+       uint8_t                 course;         /* degrees / 2 */
+       uint8_t                 hdop;           /* * 5 */
+       int16_t                 climb_rate;     /* cm/s */
+       uint16_t                h_error;        /* m */
+       uint16_t                v_error;        /* m */
+};
+
+struct ao_gps_sat_orig {
+       uint8_t         svid;
+       uint8_t         c_n_1;
+};
+
+#define AO_MAX_GPS_TRACKING    12
+
+struct ao_gps_tracking_orig {
+       uint8_t                 channels;
+       struct ao_gps_sat_orig  sats[AO_MAX_GPS_TRACKING];
+};
+
+void
+ao_gps(void);
+
+void
+ao_gps_print(__xdata struct ao_gps_orig *gps_data);
+
+void
+ao_gps_tracking_print(__xdata struct ao_gps_tracking_orig *gps_tracking_data);
+
+void
+ao_gps_init(void);
+
+/*
+ * ao_gps_report.c
+ */
+
+void
+ao_gps_report(void);
+
+void
+ao_gps_report_init(void);
+
+/*
+ * ao_gps_report_mega.c
+ */
+
+void
+ao_gps_report_mega(void);
+
+void
+ao_gps_report_mega_init(void);
+
+/*
+ * ao_telemetry_orig.c
+ */
+
+#if LEGACY_MONITOR
+struct ao_adc_orig {
+       uint16_t        tick;           /* tick when the sample was read */
+       int16_t         accel;          /* accelerometer */
+       int16_t         pres;           /* pressure sensor */
+       int16_t         temp;           /* temperature sensor */
+       int16_t         v_batt;         /* battery voltage */
+       int16_t         sense_d;        /* drogue continuity sense */
+       int16_t         sense_m;        /* main continuity sense */
+};
+
+struct ao_telemetry_orig {
+       uint16_t                serial;
+       uint16_t                flight;
+       uint8_t                 flight_state;
+       int16_t                 accel;
+       int16_t                 ground_accel;
+       union {
+               struct {
+                       int16_t                 speed;
+                       int16_t                 unused;
+               } k;
+               int32_t         flight_vel;
+       } u;
+       int16_t                 height;
+       int16_t                 ground_pres;
+       int16_t                 accel_plus_g;
+       int16_t                 accel_minus_g;
+       struct ao_adc_orig      adc;
+       struct ao_gps_orig      gps;
+       char                    callsign[AO_MAX_CALLSIGN];
+       struct ao_gps_tracking_orig     gps_tracking;
+};
+
+struct ao_telemetry_tiny {
+       uint16_t                serial;
+       uint16_t                flight;
+       uint8_t                 flight_state;
+       int16_t                 height;         /* AGL in meters */
+       int16_t                 speed;          /* in m/s * 16 */
+       int16_t                 accel;          /* in m/s² * 16 */
+       int16_t                 ground_pres;    /* sensor units */
+       struct ao_adc           adc;            /* raw ADC readings */
+       char                    callsign[AO_MAX_CALLSIGN];
+};
+
+struct ao_telemetry_orig_recv {
+       struct ao_telemetry_orig        telemetry_orig;
+       int8_t                          rssi;
+       uint8_t                         status;
+};
+
+struct ao_telemetry_tiny_recv {
+       struct ao_telemetry_tiny        telemetry_tiny;
+       int8_t                          rssi;
+       uint8_t                         status;
+};
+
+#endif /* LEGACY_MONITOR */
+
+/* Unfortunately, we've exposed the CC1111 rssi units as the 'usual' method
+ * for reporting RSSI. So, now we use these values everywhere
+ */
+#define AO_RSSI_FROM_RADIO(radio)      ((int16_t) ((int8_t) (radio) >> 1) - 74)
+#define AO_RADIO_FROM_RSSI(rssi)       (((int8_t) (rssi) + 74) << 1)
+
+/*
+ * ao_radio_recv tacks on rssi and status bytes
+ */
+
+struct ao_telemetry_raw_recv {
+       uint8_t                 packet[AO_MAX_TELEMETRY + 2];
+};
+
+/* Set delay between telemetry reports (0 to disable) */
+
+#ifdef AO_SEND_ALL_BARO
+#define AO_TELEMETRY_INTERVAL_PAD      AO_MS_TO_TICKS(100)
+#define AO_TELEMETRY_INTERVAL_FLIGHT   AO_MS_TO_TICKS(100)
+#define AO_TELEMETRY_INTERVAL_RECOVER  AO_MS_TO_TICKS(100)
+#else
+#define AO_TELEMETRY_INTERVAL_PAD      AO_MS_TO_TICKS(1000)
+#define AO_TELEMETRY_INTERVAL_FLIGHT   AO_MS_TO_TICKS(100)
+#define AO_TELEMETRY_INTERVAL_RECOVER  AO_MS_TO_TICKS(1000)
+#endif
+
+void
+ao_telemetry_set_interval(uint16_t interval);
+
+void
+ao_rdf_set(uint8_t rdf);
+
+void
+ao_telemetry_init(void);
+
+void
+ao_telemetry_orig_init(void);
+
+void
+ao_telemetry_tiny_init(void);
+
+/*
+ * ao_radio.c
+ */
+
+extern __xdata uint8_t ao_radio_dma;
+
+#ifdef PKT_APPEND_STATUS_1_CRC_OK
+#define AO_RADIO_STATUS_CRC_OK PKT_APPEND_STATUS_1_CRC_OK
+#else
+#include <ao_fec.h>
+#define AO_RADIO_STATUS_CRC_OK AO_FEC_DECODE_CRC_OK
+#endif
+
+void
+ao_radio_general_isr(void) ao_arch_interrupt(16);
+
+void
+ao_radio_send(const __xdata void *d, uint8_t size) __reentrant;
+
+uint8_t
+ao_radio_recv(__xdata void *d, uint8_t size) __reentrant;
+
+void
+ao_radio_recv_abort(void);
+
+void
+ao_radio_test(uint8_t on);
+
+/*
+ * Compute the packet length as follows:
+ *
+ * 2000 bps (for a 1kHz tone)
+ * so, for 'ms' milliseconds, we need
+ * 2 * ms bits, or ms / 4 bytes
+ */
+
+void
+ao_radio_rdf(void);
+
+void
+ao_radio_continuity(uint8_t c);
+
+void
+ao_radio_rdf_abort(void);
+
+void
+ao_radio_init(void);
+
+/*
+ * ao_monitor.c
+ */
+
+#if HAS_MONITOR
+
+extern const char const * const ao_state_names[];
+
+#define AO_MONITOR_RING        8
+
+union ao_monitor {
+       struct ao_telemetry_raw_recv    raw;
+       struct ao_telemetry_all_recv    all;
+       struct ao_telemetry_orig_recv   orig;
+       struct ao_telemetry_tiny_recv   tiny;
+};
+
+extern __xdata union ao_monitor ao_monitor_ring[AO_MONITOR_RING];
+
+#define ao_monitor_ring_next(n)        (((n) + 1) & (AO_MONITOR_RING - 1))
+
+extern __data uint8_t ao_monitoring;
+extern __data uint8_t ao_monitor_head;
+
+void
+ao_monitor(void);
+
+#define AO_MONITORING_OFF      0
+#define AO_MONITORING_ORIG     1
+
+void
+ao_monitor_set(uint8_t monitoring);
+
+void
+ao_monitor_disable(void);
+
+void
+ao_monitor_enable(void);
+
+void
+ao_monitor_init(void) __reentrant;
+
+#endif
+
+/*
+ * ao_stdio.c
+ */
+
+#define AO_READ_AGAIN  ((char) -1)
+
+struct ao_stdio {
+       char    (*pollchar)(void);
+       void    (*putchar)(char c) __reentrant;
+       void    (*flush)(void);
+       uint8_t echo;
+};
+
+extern __xdata struct ao_stdio ao_stdios[];
+extern __pdata int8_t ao_cur_stdio;
+extern __pdata int8_t ao_num_stdios;
+
+void
+flush(void);
+
+extern __xdata uint8_t ao_stdin_ready;
+
+uint8_t
+ao_echo(void);
+
+int8_t
+ao_add_stdio(char (*pollchar)(void),
+            void (*putchar)(char) __reentrant,
+            void (*flush)(void)) __reentrant;
+
+/*
+ * ao_ignite.c
+ */
+
+enum ao_igniter {
+       ao_igniter_drogue = 0,
+       ao_igniter_main = 1
+};
+
+void
+ao_ignite(enum ao_igniter igniter);
+
+enum ao_igniter_status {
+       ao_igniter_unknown,     /* unknown status (ambiguous voltage) */
+       ao_igniter_ready,       /* continuity detected */
+       ao_igniter_active,      /* igniter firing */
+       ao_igniter_open,        /* open circuit detected */
+};
+
+struct ao_ignition {
+       uint8_t request;
+       uint8_t fired;
+       uint8_t firing;
+};
+
+extern __xdata struct ao_ignition ao_ignition[2];
+
+enum ao_igniter_status
+ao_igniter_status(enum ao_igniter igniter);
+
+extern __pdata uint8_t ao_igniter_present;
+
+void
+ao_ignite_set_pins(void);
+
+void
+ao_igniter_init(void);
+
+/*
+ * ao_config.c
+ */
+
+#if AO_PYRO_NUM
+#include <ao_pyro.h>
+#endif
+
+#if HAS_FORCE_FREQ
+/*
+ * Set this to force the frequency to 434.550MHz
+ */
+extern __xdata uint8_t ao_force_freq;
+#endif
+
+#define AO_CONFIG_MAJOR        1
+#define AO_CONFIG_MINOR        12
+
+#define AO_AES_LEN 16
+
+extern __xdata uint8_t ao_config_aes_seq;
+
+struct ao_config {
+       uint8_t         major;
+       uint8_t         minor;
+       uint16_t        main_deploy;
+       int16_t         accel_plus_g;           /* changed for minor version 2 */
+       uint8_t         _legacy_radio_channel;
+       char            callsign[AO_MAX_CALLSIGN + 1];
+       uint8_t         apogee_delay;           /* minor version 1 */
+       int16_t         accel_minus_g;          /* minor version 2 */
+       uint32_t        radio_cal;              /* minor version 3 */
+       uint32_t        flight_log_max;         /* minor version 4 */
+       uint8_t         ignite_mode;            /* minor version 5 */
+       uint8_t         pad_orientation;        /* minor version 6 */
+       uint32_t        radio_setting;          /* minor version 7 */
+       uint8_t         radio_enable;           /* minor version 8 */
+       uint8_t         aes_key[AO_AES_LEN];    /* minor version 9 */
+       uint32_t        frequency;              /* minor version 10 */
+       uint16_t        apogee_lockout;         /* minor version 11 */
+#if AO_PYRO_NUM
+       struct ao_pyro  pyro[AO_PYRO_NUM];      /* minor version 12 */
+#endif
+};
+
+#define AO_IGNITE_MODE_DUAL            0
+#define AO_IGNITE_MODE_APOGEE          1
+#define AO_IGNITE_MODE_MAIN            2
+
+#define AO_PAD_ORIENTATION_ANTENNA_UP  0
+#define AO_PAD_ORIENTATION_ANTENNA_DOWN        1
+
+extern __xdata struct ao_config ao_config;
+
+#define AO_CONFIG_MAX_SIZE     128
+
+void
+_ao_config_edit_start(void);
+
+void
+_ao_config_edit_finish(void);
+
+void
+ao_config_get(void);
+
+void
+ao_config_put(void);
+
+void
+ao_config_set_radio(void);
+
+void
+ao_config_init(void);
+
+/*
+ * ao_rssi.c
+ */
+
+void
+ao_rssi_set(int rssi_value);
+
+void
+ao_rssi_init(uint8_t rssi_led);
+
+/*
+ * ao_product.c
+ *
+ * values which need to be defined for
+ * each instance of a product
+ */
+
+extern const char ao_version[];
+extern const char ao_manufacturer[];
+extern const char ao_product[];
+
+/*
+ * Fifos
+ */
+
+#define AO_FIFO_SIZE   32
+
+struct ao_fifo {
+       uint8_t insert;
+       uint8_t remove;
+       char    fifo[AO_FIFO_SIZE];
+};
+
+#define ao_fifo_insert(f,c) do { \
+       (f).fifo[(f).insert] = (c); \
+       (f).insert = ((f).insert + 1) & (AO_FIFO_SIZE-1); \
+} while(0)
+
+#define ao_fifo_remove(f,c) do {\
+       c = (f).fifo[(f).remove]; \
+       (f).remove = ((f).remove + 1) & (AO_FIFO_SIZE-1); \
+} while(0)
+
+#define ao_fifo_full(f)                ((((f).insert + 1) & (AO_FIFO_SIZE-1)) == (f).remove)
+#define ao_fifo_empty(f)       ((f).insert == (f).remove)
+
+#if PACKET_HAS_MASTER || PACKET_HAS_SLAVE
+#include <ao_packet.h>
+#endif
+
+#if HAS_BTM
+#include <ao_btm.h>
+#endif
+
+#if HAS_COMPANION
+#include <ao_companion.h>
+#endif
+
+#if HAS_LCD
+#include <ao_lcd.h>
+#endif
+
+#if HAS_AES
+#include <ao_aes.h>
+#endif
+
+/* ao_launch.c */
+
+struct ao_launch_command {
+       uint16_t        tick;
+       uint16_t        serial;
+       uint8_t         cmd;
+       uint8_t         channel;
+       uint16_t        unused;
+};
+
+#define AO_LAUNCH_QUERY                1
+
+struct ao_launch_query {
+       uint16_t        tick;
+       uint16_t        serial;
+       uint8_t         channel;
+       uint8_t         valid;
+       uint8_t         arm_status;
+       uint8_t         igniter_status;
+};
+
+#define AO_LAUNCH_ARM          2
+#define AO_LAUNCH_FIRE         3
+
+void
+ao_launch_init(void);
+
+/*
+ * ao_log_single.c
+ */
+
+#define AO_LOG_TELESCIENCE_START       ((uint8_t) 's')
+#define AO_LOG_TELESCIENCE_DATA                ((uint8_t) 'd')
+
+#define AO_LOG_TELESCIENCE_NUM_ADC     12
+
+struct ao_log_telescience {
+       uint8_t         type;
+       uint8_t         csum;
+       uint16_t        tick;
+       uint16_t        tm_tick;
+       uint8_t         tm_state;
+       uint8_t         unused;
+       uint16_t        adc[AO_LOG_TELESCIENCE_NUM_ADC];
+};
+
+#define AO_LOG_SINGLE_SIZE             32
+
+union ao_log_single {
+       struct ao_log_telescience       telescience;
+       union ao_telemetry_all          telemetry;
+       uint8_t                         bytes[AO_LOG_SINGLE_SIZE];
+};
+
+extern __xdata union ao_log_single     ao_log_single_write_data;
+extern __xdata union ao_log_single     ao_log_single_read_data;
+
+void
+ao_log_single_extra_query(void);
+
+void
+ao_log_single_list(void);
+
+void
+ao_log_single_main(void);
+
+uint8_t
+ao_log_single_write(void);
+
+uint8_t
+ao_log_single_read(uint32_t pos);
+
+void
+ao_log_single_start(void);
+
+void
+ao_log_single_stop(void);
+
+void
+ao_log_single_restart(void);
+
+void
+ao_log_single_set(void);
+
+void
+ao_log_single_delete(void);
+
+void
+ao_log_single_init(void);
+
+void
+ao_log_single(void);
+
+/*
+ * ao_pyro_slave.c
+ */
+
+#define AO_TELEPYRO_NUM_ADC    9
+
+#ifndef ao_xmemcpy
+#define ao_xmemcpy(d,s,c) memcpy(d,s,c)
+#define ao_xmemset(d,v,c) memset(d,v,c)
+#define ao_xmemcmp(d,s,c) memcmp(d,s,c)
+#endif
+
+/*
+ * ao_terraui.c
+ */
+
+void
+ao_terraui_init(void);
+
+/*
+ * ao_battery.c
+ */
+
+#ifdef BATTERY_PIN
+void
+ao_battery_isr(void) ao_arch_interrupt(1);
+
+uint16_t
+ao_battery_get(void);
+
+void
+ao_battery_init(void);
+#endif /* BATTERY_PIN */
+
+/*
+ * ao_sqrt.c
+ */
+
+uint32_t
+ao_sqrt(uint32_t op);
+
+/*
+ * ao_freq.c
+ */
+
+int32_t ao_freq_to_set(int32_t freq, int32_t cal) __reentrant;
+
+/*
+ * ao_ms5607.c
+ */
+
+void ao_ms5607_init(void);
+
+#include <ao_arch_funcs.h>
+
+#endif /* _AO_H_ */
diff --git a/src/core/ao_adc.h b/src/core/ao_adc.h
new file mode 100644 (file)
index 0000000..0dd8708
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ADC_H_
+#define _AO_ADC_H_
+
+#include <ao_data.h>
+
+/* Trigger a conversion sequence (called from the timer interrupt) */
+void
+ao_adc_poll(void);
+
+/* Suspend the current task until another A/D sample is converted */
+void
+ao_adc_sleep(void);
+
+/* Get a copy of the last complete sample set */
+void
+ao_data_get(__xdata struct ao_data *packet);
+
+/* Initialize the A/D converter */
+void
+ao_adc_init(void);
+
+#endif /* _AO_ADC_H_ */
diff --git a/src/core/ao_aes.h b/src/core/ao_aes.h
new file mode 100644 (file)
index 0000000..c47bc2d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_AES_H_
+#define _AO_AES_H_
+
+/* ao_aes.c */
+
+extern __xdata uint8_t ao_aes_mutex;
+
+/* AES keys and blocks are 128 bits */
+
+enum ao_aes_mode {
+       ao_aes_mode_cbc_mac
+};
+
+#if HAS_AES
+#ifdef SDCC
+void
+ao_aes_isr(void) __interrupt 4;
+#endif
+#endif
+
+void
+ao_aes_set_mode(enum ao_aes_mode mode);
+
+void
+ao_aes_set_key(__xdata uint8_t *in);
+
+void
+ao_aes_zero_iv(void);
+
+void
+ao_aes_run(__xdata uint8_t *in,
+          __xdata uint8_t *out);
+
+void
+ao_aes_init(void);
+
+#endif /* _AO_AES_H_ */
diff --git a/src/core/ao_beep.h b/src/core/ao_beep.h
new file mode 100644 (file)
index 0000000..55f6117
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BEEP_H_
+#define _AO_BEEP_H_
+
+/*
+ * ao_beep.c
+ */
+
+/*
+ * Various pre-defined beep frequencies
+ *
+ * frequency = 1/2 (24e6/32) / beep
+ */
+
+#define AO_BEEP_LOW    150     /* 2500Hz */
+#define AO_BEEP_MID    94      /* 3989Hz */
+#define AO_BEEP_HIGH   75      /* 5000Hz */
+#define AO_BEEP_OFF    0       /* off */
+
+#define AO_BEEP_g      240     /* 1562.5Hz */
+#define AO_BEEP_gs     227     /* 1652Hz (1655Hz) */
+#define AO_BEEP_aa     214     /* 1752Hz (1754Hz) */
+#define AO_BEEP_bbf    202     /* 1856Hz (1858Hz) */
+#define AO_BEEP_bb     190     /* 1974Hz (1969Hz) */
+#define AO_BEEP_cc     180     /* 2083Hz (2086Hz) */
+#define AO_BEEP_ccs    170     /* 2205Hz (2210Hz) */
+#define AO_BEEP_dd     160     /* 2344Hz (2341Hz) */
+#define AO_BEEP_eef    151     /* 2483Hz (2480Hz) */
+#define AO_BEEP_ee     143     /* 2622Hz (2628Hz) */
+#define AO_BEEP_ff     135     /* 2778Hz (2784Hz) */
+#define AO_BEEP_ffs    127     /* 2953Hz (2950Hz) */
+#define AO_BEEP_gg     120     /* 3125Hz */
+#define AO_BEEP_ggs    113     /* 3319Hz (3311Hz) */
+#define AO_BEEP_aaa    107     /* 3504Hz (3508Hz) */
+#define AO_BEEP_bbbf   101     /* 3713Hz (3716Hz) */
+#define AO_BEEP_bbb    95      /* 3947Hz (3937Hz) */
+#define AO_BEEP_ccc    90      /* 4167Hz (4171Hz) */
+#define AO_BEEP_cccs   85      /* 4412Hz (4419Hz) */
+#define AO_BEEP_ddd    80      /* 4688Hz (4682Hz) */
+#define AO_BEEP_eeef   76      /* 4934Hz (4961Hz) */
+#define AO_BEEP_eee    71      /* 5282Hz (5256Hz) */
+#define AO_BEEP_fff    67      /* 5597Hz (5568Hz) */
+#define AO_BEEP_fffs   64      /* 5859Hz (5899Hz) */
+#define AO_BEEP_ggg    60      /* 6250Hz */
+
+/* Set the beeper to the specified tone */
+void
+ao_beep(uint8_t beep);
+
+/* Turn on the beeper for the specified time */
+void
+ao_beep_for(uint8_t beep, uint16_t ticks) __reentrant;
+
+/* Initialize the beeper */
+void
+ao_beep_init(void);
+
+#endif /* _AO_BEEP_H_ */
diff --git a/src/core/ao_btm.h b/src/core/ao_btm.h
new file mode 100644 (file)
index 0000000..484e5d7
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BTM_H_
+#define _AO_BTM_H_
+
+/* ao_btm.c */
+
+/* If bt_link is on P2, this interrupt is shared by USB, so the USB
+ * code calls this function. Otherwise, it's a regular ISR.
+ */
+
+void
+ao_btm_isr(void)
+#if BT_LINK_ON_P1
+       __interrupt 15
+#endif
+       ;
+void
+ao_btm_init(void);
+
+#endif /* _AO_BTM_H_ */
diff --git a/src/core/ao_cmd.c b/src/core/ao_cmd.c
new file mode 100644 (file)
index 0000000..1814cec
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+__pdata uint16_t ao_cmd_lex_i;
+__pdata uint32_t ao_cmd_lex_u32;
+__pdata char   ao_cmd_lex_c;
+__pdata enum ao_cmd_status ao_cmd_status;
+
+#define CMD_LEN        48
+
+static __xdata char    cmd_line[CMD_LEN];
+static __pdata uint8_t cmd_len;
+static __pdata uint8_t cmd_i;
+
+static void
+put_string(__code char *s)
+{
+       char    c;
+       while ((c = *s++))
+               putchar(c);
+}
+
+static void
+backspace(void)
+{
+       put_string ("\010 \010");
+}
+
+static void
+readline(void)
+{
+       char c;
+       if (ao_echo())
+               put_string("> ");
+       cmd_len = 0;
+       for (;;) {
+               flush();
+               c = getchar();
+               /* backspace/delete */
+               if (c == '\010' || c == '\177') {
+                       if (cmd_len != 0) {
+                               if (ao_echo())
+                                       backspace();
+                               --cmd_len;
+                       }
+                       continue;
+               }
+
+               /* ^U */
+               if (c == '\025') {
+                       while (cmd_len != 0) {
+                               if (ao_echo())
+                                       backspace();
+                               --cmd_len;
+                       }
+                       continue;
+               }
+
+               /* map CR to NL */
+               if (c == '\r')
+                       c = '\n';
+
+               if (c == '\n') {
+                       if (ao_echo())
+                               putchar('\n');
+                       break;
+               }
+
+               if (cmd_len >= CMD_LEN - 2)
+                       continue;
+               cmd_line[cmd_len++] = c;
+               if (ao_echo())
+                       putchar(c);
+       }
+       cmd_line[cmd_len++] = '\n';
+       cmd_line[cmd_len++] = '\0';
+       cmd_i = 0;
+}
+
+void
+ao_cmd_lex(void)
+{
+       ao_cmd_lex_c = '\n';
+       if (cmd_i < cmd_len)
+               ao_cmd_lex_c = cmd_line[cmd_i++];
+}
+
+static void
+putnibble(uint8_t v)
+{
+       if (v < 10)
+               putchar(v + '0');
+       else
+               putchar(v + ('a' - 10));
+}
+
+void
+ao_cmd_put16(uint16_t v)
+{
+       ao_cmd_put8(v >> 8);
+       ao_cmd_put8(v);
+}
+
+void
+ao_cmd_put8(uint8_t v)
+{
+       putnibble((v >> 4) & 0xf);
+       putnibble(v & 0xf);
+}
+
+uint8_t
+ao_cmd_is_white(void)
+{
+       return ao_cmd_lex_c == ' ' || ao_cmd_lex_c == '\t';
+}
+
+void
+ao_cmd_white(void)
+{
+       while (ao_cmd_is_white())
+               ao_cmd_lex();
+}
+
+int8_t
+ao_cmd_hexchar(char c)
+{
+       if ('0' <= c && c <= '9')
+               return (c - '0');
+       if ('a' <= c && c <= 'f')
+               return (c - 'a' + 10);
+       if ('A' <= c && c <= 'F')
+               return (c - 'A' + 10);
+       return -1;
+}
+
+void
+ao_cmd_hexbyte(void)
+{
+       uint8_t i;
+       int8_t  n;
+
+       ao_cmd_lex_i = 0;
+       ao_cmd_white();
+       for (i = 0; i < 2; i++) {
+               n = ao_cmd_hexchar(ao_cmd_lex_c);
+               if (n < 0) {
+                       ao_cmd_status = ao_cmd_syntax_error;
+                       break;
+               }
+               ao_cmd_lex_i = (ao_cmd_lex_i << 4) | n;
+               ao_cmd_lex();
+       }
+}
+
+void
+ao_cmd_hex(void)
+{
+       __pdata uint8_t r = ao_cmd_lex_error;
+       int8_t  n;
+
+       ao_cmd_lex_i = 0;
+       ao_cmd_white();
+       for(;;) {
+               n = ao_cmd_hexchar(ao_cmd_lex_c);
+               if (n < 0)
+                       break;
+               ao_cmd_lex_i = (ao_cmd_lex_i << 4) | n;
+               r = ao_cmd_success;
+               ao_cmd_lex();
+       }
+       if (r != ao_cmd_success)
+               ao_cmd_status = r;
+}
+
+void
+ao_cmd_decimal(void)
+{
+       __pdata uint8_t r = ao_cmd_lex_error;
+
+       ao_cmd_lex_u32 = 0;
+       ao_cmd_white();
+       for(;;) {
+               if ('0' <= ao_cmd_lex_c && ao_cmd_lex_c <= '9')
+                       ao_cmd_lex_u32 = (ao_cmd_lex_u32 * 10) + (ao_cmd_lex_c - '0');
+               else
+                       break;
+               r = ao_cmd_success;
+               ao_cmd_lex();
+       }
+       if (r != ao_cmd_success)
+               ao_cmd_status = r;
+       ao_cmd_lex_i = (uint16_t) ao_cmd_lex_u32;
+}
+
+uint8_t
+ao_match_word(__code char *word)
+{
+       while (*word) {
+               if (ao_cmd_lex_c != *word) {
+                       ao_cmd_status = ao_cmd_syntax_error;
+                       return 0;
+               }
+               word++;
+               ao_cmd_lex();
+       }
+       return 1;
+}
+
+static void
+echo(void)
+{
+       ao_cmd_hex();
+       if (ao_cmd_status == ao_cmd_success)
+               ao_stdios[ao_cur_stdio].echo = ao_cmd_lex_i != 0;
+}
+
+static void
+ao_reboot(void)
+{
+       ao_cmd_white();
+       if (!ao_match_word("eboot"))
+               return;
+       /* Delay waiting for the packet master to be turned off
+        * so that we don't end up back in idle mode because we
+        * received a packet after boot.
+        */
+       flush();
+       ao_delay(AO_SEC_TO_TICKS(1));
+       ao_arch_reboot();
+       ao_panic(AO_PANIC_REBOOT);
+}
+
+static void
+version(void)
+{
+       printf("manufacturer     %s\n", ao_manufacturer);
+       printf("product          %s\n", ao_product);
+       printf("serial-number    %u\n", ao_serial_number);
+#if HAS_LOG
+       printf("log-format       %u\n", ao_log_format);
+#endif
+#if HAS_MS5607
+       ao_ms5607_info();
+#endif
+       printf("software-version %s\n", ao_version);
+}
+
+#ifndef NUM_CMDS
+#define NUM_CMDS       11
+#endif
+
+static __code struct ao_cmds   *__xdata (ao_cmds[NUM_CMDS]);
+static __pdata uint8_t         ao_ncmds;
+
+static void
+help(void)
+{
+       __pdata uint8_t cmds;
+       __pdata uint8_t cmd;
+       __code struct ao_cmds * __pdata cs;
+       const char *h;
+
+       for (cmds = 0; cmds < ao_ncmds; cmds++) {
+               cs = ao_cmds[cmds];
+               for (cmd = 0; cs[cmd].func; cmd++) {
+                       h = cs[cmd].help;
+                       printf("%-45s %s\n", h, h + 1 + strlen(h));
+               }
+       }
+}
+
+static void
+report(void)
+{
+       switch(ao_cmd_status) {
+       case ao_cmd_lex_error:
+       case ao_cmd_syntax_error:
+               puts("Syntax error");
+               ao_cmd_status = 0;
+       default:
+               break;
+       }
+}
+
+void
+ao_cmd_register(__code struct ao_cmds *cmds)
+{
+       if (ao_ncmds >= NUM_CMDS)
+               ao_panic(AO_PANIC_CMD);
+       ao_cmds[ao_ncmds++] = cmds;
+}
+
+void
+ao_cmd(void)
+{
+       __pdata char    c;
+       uint8_t cmd, cmds;
+       __code struct ao_cmds * __xdata cs;
+       void (*__xdata func)(void);
+
+       for (;;) {
+               readline();
+               ao_cmd_lex();
+               ao_cmd_white();
+               c = ao_cmd_lex_c;
+               ao_cmd_lex();
+               if (c == '\r' || c == '\n')
+                       continue;
+               func = (void (*)(void)) NULL;
+               for (cmds = 0; cmds < ao_ncmds; cmds++) {
+                       cs = ao_cmds[cmds];
+                       for (cmd = 0; cs[cmd].func; cmd++)
+                               if (cs[cmd].help[0] == c) {
+                                       func = cs[cmd].func;
+                                       break;
+                               }
+                       if (func)
+                               break;
+               }
+               if (func)
+                       (*func)();
+               else
+                       ao_cmd_status = ao_cmd_syntax_error;
+               report();
+       }
+}
+
+__xdata struct ao_task ao_cmd_task;
+
+__code struct ao_cmds  ao_base_cmds[] = {
+       { help,         "?\0Help" },
+       { ao_task_info, "T\0Tasks" },
+       { echo,         "E <0 off, 1 on>\0Echo" },
+       { ao_reboot,    "r eboot\0Reboot" },
+       { version,      "v\0Version" },
+       { 0,    NULL },
+};
+
+void
+ao_cmd_init(void)
+{
+       ao_cmd_register(&ao_base_cmds[0]);
+       ao_add_task(&ao_cmd_task, ao_cmd, "cmd");
+}
diff --git a/src/core/ao_companion.h b/src/core/ao_companion.h
new file mode 100644 (file)
index 0000000..035325a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_COMPANION_H_
+#define _AO_COMPANION_H_
+
+/* ao_companion.c */
+
+#define AO_COMPANION_SETUP             1
+#define AO_COMPANION_FETCH             2
+#define AO_COMPANION_NOTIFY            3
+
+struct ao_companion_command {
+       uint8_t         command;
+       uint8_t         flight_state;
+       uint16_t        tick;
+       uint16_t        serial;
+       uint16_t        flight;
+       int16_t         accel;
+       int16_t         speed;
+       int16_t         height;
+       int16_t         motor_number;
+};
+
+struct ao_companion_setup {
+       uint16_t        board_id;
+       uint16_t        board_id_inverse;
+       uint8_t         update_period;
+       uint8_t         channels;
+};
+
+extern __pdata uint8_t                         ao_companion_running;
+extern __xdata uint8_t                         ao_companion_mutex;
+extern __xdata struct ao_companion_command     ao_companion_command;
+extern __xdata struct ao_companion_setup       ao_companion_setup;
+extern __xdata uint16_t                                ao_companion_data[AO_COMPANION_MAX_CHANNELS];
+
+void
+ao_companion_init(void);
+
+#endif /* _AO_COMPANION_H_ */
diff --git a/src/core/ao_config.c b/src/core/ao_config.c
new file mode 100644 (file)
index 0000000..ce855ad
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_log.h"
+#include <ao_storage.h>
+#if HAS_FLIGHT
+#include <ao_sample.h>
+#include <ao_data.h>
+#endif
+
+__xdata struct ao_config ao_config;
+__pdata uint8_t ao_config_loaded;
+__pdata uint8_t ao_config_dirty;
+__xdata uint8_t ao_config_mutex;
+
+#define AO_CONFIG_DEFAULT_MAIN_DEPLOY  250
+#define AO_CONFIG_DEFAULT_RADIO_CHANNEL        0
+#define AO_CONFIG_DEFAULT_CALLSIGN     "N0CALL"
+#define AO_CONFIG_DEFAULT_ACCEL_ZERO_G 16000
+#define AO_CONFIG_DEFAULT_APOGEE_DELAY 0
+#define AO_CONFIG_DEFAULT_IGNITE_MODE  AO_IGNITE_MODE_DUAL
+#define AO_CONFIG_DEFAULT_PAD_ORIENTATION      AO_PAD_ORIENTATION_ANTENNA_UP
+#if HAS_EEPROM
+#ifndef USE_INTERNAL_FLASH
+#error Please define USE_INTERNAL_FLASH
+#endif
+#endif
+#ifndef AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX
+#if USE_INTERNAL_FLASH
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       ao_storage_config
+#else
+#define AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX       ((uint32_t) 192 * (uint32_t) 1024)
+#endif
+#endif
+
+#if HAS_EEPROM
+static void
+_ao_config_put(void)
+{
+       ao_storage_setup();
+       ao_storage_erase(ao_storage_config);
+       ao_storage_write(ao_storage_config, &ao_config, sizeof (ao_config));
+#if HAS_FLIGHT
+       ao_log_write_erase(0);
+#endif
+       ao_storage_flush();
+}
+
+void
+ao_config_put(void)
+{
+       ao_mutex_get(&ao_config_mutex);
+       _ao_config_put();
+       ao_mutex_put(&ao_config_mutex);
+}
+#endif
+
+#if HAS_RADIO
+void
+ao_config_set_radio(void)
+{
+       ao_config.radio_setting = ao_freq_to_set(ao_config.frequency, ao_config.radio_cal);
+}
+#endif /* HAS_RADIO */
+
+static void
+_ao_config_get(void)
+{
+       uint8_t minor;
+
+       if (ao_config_loaded)
+               return;
+#if HAS_EEPROM
+       /* Yes, I know ao_storage_read calls ao_storage_setup,
+        * but ao_storage_setup *also* sets ao_storage_config, which we
+        * need before calling ao_storage_read here
+        */
+       ao_storage_setup();
+       ao_storage_read(ao_storage_config, &ao_config, sizeof (ao_config));
+#endif
+       if (ao_config.major != AO_CONFIG_MAJOR) {
+               ao_config.major = AO_CONFIG_MAJOR;
+               ao_config.minor = 0;
+
+               /* Version 0 stuff */
+               ao_config.main_deploy = AO_CONFIG_DEFAULT_MAIN_DEPLOY;
+               ao_xmemset(&ao_config.callsign, '\0', sizeof (ao_config.callsign));
+               ao_xmemcpy(&ao_config.callsign, CODE_TO_XDATA(AO_CONFIG_DEFAULT_CALLSIGN),
+                      sizeof(AO_CONFIG_DEFAULT_CALLSIGN) - 1);
+       }
+       minor = ao_config.minor;
+       if (minor != AO_CONFIG_MINOR) {
+               /* Fixups for minor version 1 */
+               if (minor < 1)
+                       ao_config.apogee_delay = AO_CONFIG_DEFAULT_APOGEE_DELAY;
+               /* Fixups for minor version 2 */
+               if (minor < 2) {
+                       ao_config.accel_plus_g = 0;
+                       ao_config.accel_minus_g = 0;
+               }
+               /* Fixups for minor version 3 */
+#if HAS_RADIO
+               if (minor < 3)
+                       ao_config.radio_cal = ao_radio_cal;
+#endif
+               /* Fixups for minor version 4 */
+               if (minor < 4)
+                       ao_config.flight_log_max = AO_CONFIG_DEFAULT_FLIGHT_LOG_MAX;
+               /* Fixupes for minor version 5 */
+               if (minor < 5)
+                       ao_config.ignite_mode = AO_CONFIG_DEFAULT_IGNITE_MODE;
+               if (minor < 6)
+                       ao_config.pad_orientation = AO_CONFIG_DEFAULT_PAD_ORIENTATION;
+               if (minor < 8)
+                       ao_config.radio_enable = TRUE;
+               if (minor < 9)
+                       ao_xmemset(&ao_config.aes_key, '\0', AO_AES_LEN);
+               if (minor < 10)
+                       ao_config.frequency = 434550;
+               if (minor < 11)
+                       ao_config.apogee_lockout = 0;
+#if AO_PYRO_NUM
+               if (minor < 12)
+                       memset(&ao_config.pyro, '\0', sizeof (ao_config.pyro));
+#endif
+               ao_config.minor = AO_CONFIG_MINOR;
+               ao_config_dirty = 1;
+       }
+#if HAS_RADIO
+#if HAS_FORCE_FREQ
+       if (ao_force_freq)
+               ao_config.frequency = 434550;
+#endif
+       ao_config_set_radio();
+#endif
+       ao_config_loaded = 1;
+}
+
+void
+_ao_config_edit_start(void)
+{
+       ao_mutex_get(&ao_config_mutex);
+       _ao_config_get();
+}
+
+void
+_ao_config_edit_finish(void)
+{
+       ao_config_dirty = 1;
+       ao_mutex_put(&ao_config_mutex);
+}
+
+void
+ao_config_get(void)
+{
+       _ao_config_edit_start();
+       ao_mutex_put(&ao_config_mutex);
+}
+
+void
+ao_config_callsign_show(void)
+{
+       printf ("Callsign: \"%s\"\n", ao_config.callsign);
+}
+
+void
+ao_config_callsign_set(void) __reentrant
+{
+       uint8_t c;
+       static __xdata char callsign[AO_MAX_CALLSIGN + 1];
+
+       ao_xmemset(callsign, '\0', sizeof callsign);
+       ao_cmd_white();
+       c = 0;
+       while (ao_cmd_lex_c != '\n') {
+               if (c < AO_MAX_CALLSIGN)
+                       callsign[c++] = ao_cmd_lex_c;
+               else
+                       ao_cmd_status = ao_cmd_lex_error;
+               ao_cmd_lex();
+       }
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_xmemcpy(&ao_config.callsign, &callsign,
+              AO_MAX_CALLSIGN + 1);
+       _ao_config_edit_finish();
+}
+
+#if HAS_RADIO
+void
+ao_config_frequency_show(void) __reentrant
+{
+       printf("Frequency: %ld\n",
+              ao_config.frequency);
+}
+
+void
+ao_config_frequency_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.frequency = ao_cmd_lex_u32;
+       ao_config_set_radio();
+       _ao_config_edit_finish();
+       ao_radio_recv_abort();
+}
+#endif
+
+#if HAS_FLIGHT
+
+void
+ao_config_main_deploy_show(void) __reentrant
+{
+       printf("Main deploy: %d meters\n",
+              ao_config.main_deploy);
+}
+
+void
+ao_config_main_deploy_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.main_deploy = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+
+#if HAS_ACCEL
+void
+ao_config_accel_calibrate_show(void) __reentrant
+{
+       printf("Accel cal +1g: %d -1g: %d\n",
+              ao_config.accel_plus_g, ao_config.accel_minus_g);
+}
+
+#define ACCEL_CALIBRATE_SAMPLES        1024
+#define ACCEL_CALIBRATE_SHIFT  10
+
+static int16_t
+ao_config_accel_calibrate_auto(char *orientation) __reentrant
+{
+       uint16_t        i;
+       int32_t         accel_total;
+       uint8_t         cal_data_ring;
+
+       printf("Orient antenna %s and press a key...", orientation);
+       flush();
+       (void) getchar();
+       puts("\r\n"); flush();
+       puts("Calibrating..."); flush();
+       i = ACCEL_CALIBRATE_SAMPLES;
+       accel_total = 0;
+       cal_data_ring = ao_sample_data;
+       while (i) {
+               ao_sleep(DATA_TO_XDATA(&ao_sample_data));
+               while (i && cal_data_ring != ao_sample_data) {
+                       accel_total += (int32_t) ao_data_accel(&ao_data_ring[cal_data_ring]);
+                       cal_data_ring = ao_data_ring_next(cal_data_ring);
+                       i--;
+               }
+       }
+       return accel_total >> ACCEL_CALIBRATE_SHIFT;
+}
+
+void
+ao_config_accel_calibrate_set(void) __reentrant
+{
+       int16_t up, down;
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       if (ao_cmd_lex_i == 0) {
+               up = ao_config_accel_calibrate_auto("up");
+               down = ao_config_accel_calibrate_auto("down");
+       } else {
+               up = ao_cmd_lex_i;
+               ao_cmd_decimal();
+               if (ao_cmd_status != ao_cmd_success)
+                       return;
+               down = ao_cmd_lex_i;
+       }
+       if (up >= down) {
+               printf("Invalid accel: up (%d) down (%d)\n",
+                      up, down);
+               return;
+       }
+       _ao_config_edit_start();
+       ao_config.accel_plus_g = up;
+       ao_config.accel_minus_g = down;
+       _ao_config_edit_finish();
+}
+#endif /* HAS_ACCEL */
+
+void
+ao_config_apogee_delay_show(void) __reentrant
+{
+       printf("Apogee delay: %d seconds\n",
+              ao_config.apogee_delay);
+}
+
+void
+ao_config_apogee_delay_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.apogee_delay = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+
+void
+ao_config_apogee_lockout_show(void) __reentrant
+{
+       printf ("Apogee lockout: %d seconds\n",
+               ao_config.apogee_lockout);
+}
+
+void
+ao_config_apogee_lockout_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.apogee_lockout = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+
+#endif /* HAS_FLIGHT */
+
+#if HAS_RADIO
+void
+ao_config_radio_cal_show(void) __reentrant
+{
+       printf("Radio cal: %ld\n", ao_config.radio_cal);
+}
+
+void
+ao_config_radio_cal_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.radio_cal = ao_cmd_lex_u32;
+       ao_config_set_radio();
+       _ao_config_edit_finish();
+}
+#endif
+
+#if HAS_LOG
+void
+ao_config_log_show(void) __reentrant
+{
+       printf("Max flight log: %d kB\n", (int16_t) (ao_config.flight_log_max >> 10));
+}
+
+void
+ao_config_log_set(void) __reentrant
+{
+       uint16_t        block = (uint16_t) (ao_storage_block >> 10);
+       uint16_t        config = (uint16_t) (ao_storage_config >> 10);
+
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       if (ao_log_present())
+               printf("Storage must be empty before changing log size\n");
+       else if (block > 1024 && (ao_cmd_lex_i & (block - 1)))
+               printf("Flight log size must be multiple of %d kB\n", block);
+       else if (ao_cmd_lex_i > config)
+               printf("Flight log max %d kB\n", config);
+       else {
+               _ao_config_edit_start();
+               ao_config.flight_log_max = (uint32_t) ao_cmd_lex_i << 10;
+               _ao_config_edit_finish();
+       }
+}
+#endif /* HAS_LOG */
+
+#if HAS_IGNITE
+void
+ao_config_ignite_mode_show(void) __reentrant
+{
+       printf("Ignite mode: %d\n", ao_config.ignite_mode);
+}
+
+void
+ao_config_ignite_mode_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.ignite_mode = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+#endif
+
+#if HAS_ACCEL
+void
+ao_config_pad_orientation_show(void) __reentrant
+{
+       printf("Pad orientation: %d\n", ao_config.pad_orientation);
+}
+
+void
+ao_config_pad_orientation_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_cmd_lex_i &= 1;
+       if (ao_config.pad_orientation != ao_cmd_lex_i) {
+               uint16_t t;
+               t = ao_config.accel_plus_g;
+               ao_config.accel_plus_g = 0x7fff - ao_config.accel_minus_g;
+               ao_config.accel_minus_g = 0x7fff - t;
+       }
+       ao_config.pad_orientation = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+#endif
+
+#if HAS_RADIO
+void
+ao_config_radio_enable_show(void) __reentrant
+{
+       printf("Radio enable: %d\n", ao_config.radio_enable);
+}
+
+void
+ao_config_radio_enable_set(void) __reentrant
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.radio_enable = ao_cmd_lex_i;
+       _ao_config_edit_finish();
+}
+#endif /* HAS_RADIO */
+       
+#if HAS_AES
+
+__xdata uint8_t        ao_config_aes_seq = 1;
+
+void
+ao_config_key_show(void) __reentrant
+{
+       uint8_t i;
+       printf("AES key: ");
+       for (i = 0; i < AO_AES_LEN; i++)
+               printf ("%02x", ao_config.aes_key[i]);
+       printf("\n");
+}
+
+void
+ao_config_key_set(void) __reentrant
+{
+       uint8_t i;
+
+       _ao_config_edit_start();
+       for (i = 0; i < AO_AES_LEN; i++) {
+               ao_cmd_hexbyte();
+               if (ao_cmd_status != ao_cmd_success)
+                       break;
+               ao_config.aes_key[i] = ao_cmd_lex_i;
+       }
+       ++ao_config_aes_seq;
+       _ao_config_edit_finish();
+}
+#endif
+
+struct ao_config_var {
+       __code char     *str;
+       void            (*set)(void) __reentrant;
+       void            (*show)(void) __reentrant;
+};
+
+static void
+ao_config_help(void) __reentrant;
+
+static void
+ao_config_show(void) __reentrant;
+
+static void
+ao_config_write(void) __reentrant;
+
+__code struct ao_config_var ao_config_vars[] = {
+#if HAS_FLIGHT
+       { "m <meters>\0Main deploy (m)",
+         ao_config_main_deploy_set,    ao_config_main_deploy_show, },
+       { "d <delay>\0Apogee delay (s)",
+         ao_config_apogee_delay_set,   ao_config_apogee_delay_show },
+       { "L <seconds>\0Apogee detect lockout (s)",
+         ao_config_apogee_lockout_set, ao_config_apogee_lockout_show, },
+#endif /* HAS_FLIGHT */
+#if HAS_RADIO
+       { "F <freq>\0Frequency (kHz)",
+         ao_config_frequency_set, ao_config_frequency_show },
+       { "c <call>\0Callsign (8 char max)",
+         ao_config_callsign_set,       ao_config_callsign_show },
+       { "e <0 disable, 1 enable>\0Enable telemetry and RDF",
+         ao_config_radio_enable_set, ao_config_radio_enable_show },
+#endif /* HAS_RADIO */
+#if HAS_ACCEL
+       { "a <+g> <-g>\0Accel calib (0 for auto)",
+         ao_config_accel_calibrate_set,ao_config_accel_calibrate_show },
+#endif /* HAS_ACCEL */
+#if HAS_RADIO
+       { "f <cal>\0Radio calib (cal = rf/(xtal/2^16))",
+         ao_config_radio_cal_set,      ao_config_radio_cal_show },
+#endif /* HAS_RADIO */
+#if HAS_LOG
+       { "l <size>\0Flight log size (kB)",
+         ao_config_log_set,            ao_config_log_show },
+#endif
+#if HAS_IGNITE
+       { "i <0 dual, 1 apogee, 2 main>\0Set igniter mode",
+         ao_config_ignite_mode_set,    ao_config_ignite_mode_show },
+#endif
+#if HAS_ACCEL
+       { "o <0 antenna up, 1 antenna down>\0Set pad orientation",
+         ao_config_pad_orientation_set,ao_config_pad_orientation_show },
+#endif
+#if HAS_AES
+       { "k <32 hex digits>\0Set AES encryption key",
+         ao_config_key_set, ao_config_key_show },
+#endif
+#if AO_PYRO_NUM
+       { "P <n,?>\0Configure pyro channels",
+         ao_pyro_set, ao_pyro_show },
+#endif
+       { "s\0Show",
+         ao_config_show,               0 },
+#if HAS_EEPROM
+       { "w\0Write to eeprom",
+         ao_config_write,              0 },
+#endif
+       { "?\0Help",
+         ao_config_help,               0 },
+       { 0, 0, 0 }
+};
+
+void
+ao_config_set(void)
+{
+       char    c;
+       uint8_t cmd;
+
+       ao_cmd_white();
+       c = ao_cmd_lex_c;
+       ao_cmd_lex();
+       for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
+               if (ao_config_vars[cmd].str[0] == c) {
+                       (*ao_config_vars[cmd].set)();
+                       return;
+               }
+       ao_cmd_status = ao_cmd_syntax_error;
+}
+
+static void
+ao_config_help(void) __reentrant
+{
+       uint8_t cmd;
+       for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
+               printf("%-20s %s\n",
+                      ao_config_vars[cmd].str,
+                      ao_config_vars[cmd].str+1+
+                      strlen(ao_config_vars[cmd].str));
+}
+
+static void
+ao_config_show(void) __reentrant
+{
+       uint8_t cmd;
+       ao_config_get();
+       printf("Config version: %d.%d\n",
+              ao_config.major, ao_config.minor);
+       for (cmd = 0; ao_config_vars[cmd].str != NULL; cmd++)
+               if (ao_config_vars[cmd].show)
+                       (*ao_config_vars[cmd].show)();
+}
+
+#if HAS_EEPROM
+static void
+ao_config_write(void) __reentrant
+{
+       uint8_t saved = 0;
+       ao_mutex_get(&ao_config_mutex);
+       if (ao_config_dirty) {
+               _ao_config_put();
+               ao_config_dirty = 0;
+               saved = 1;
+       }
+       ao_mutex_put(&ao_config_mutex);
+       if (saved)
+               puts("Saved");
+       else
+               puts("Nothing to save");
+}
+#endif
+
+__code struct ao_cmds ao_config_cmds[] = {
+       { ao_config_set,        "c <var> <value>\0Set config (? for help, s to show)" },
+       { 0, NULL },
+};
+
+void
+ao_config_init(void)
+{
+       ao_cmd_register(&ao_config_cmds[0]);
+}
diff --git a/src/core/ao_convert.c b/src/core/ao_convert.c
new file mode 100644 (file)
index 0000000..aa9b5f4
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#if !defined(AO_CONVERT_TEST) && !defined(AO_FLIGHT_TEST)
+#include "ao.h"
+#endif
+
+static const int16_t altitude_table[] = {
+#include "altitude.h"
+};
+
+#define ALT_FRAC_SCALE (1 << ALT_FRAC_BITS)
+#define ALT_FRAC_MASK  (ALT_FRAC_SCALE - 1)
+
+int16_t
+ao_pres_to_altitude(int16_t pres) __reentrant
+{
+       uint8_t o;
+       int16_t part;
+
+       if (pres < 0)
+               pres = 0;
+       o = pres >> ALT_FRAC_BITS;
+       part = pres & ALT_FRAC_MASK;
+
+       return ((int32_t) altitude_table[o] * (ALT_FRAC_SCALE - part) +
+               (int32_t) altitude_table[o+1] * part + (ALT_FRAC_SCALE >> 1)) >> ALT_FRAC_BITS;
+}
+
+#if AO_NEED_ALTITUDE_TO_PRES
+int16_t
+ao_altitude_to_pres(int16_t alt) __reentrant
+{
+       int16_t span, sub_span;
+       uint8_t l, h, m;
+       int32_t pres;
+
+       l = 0;
+       h = NALT - 1;
+       while ((h - l) != 1) {
+               m = (l + h) >> 1;
+               if (altitude_table[m] < alt)
+                       h = m;
+               else
+                       l = m;
+       }
+       span = altitude_table[l] - altitude_table[h];
+       sub_span = altitude_table[l] - alt;
+       pres = ((((int32_t) l * (span - sub_span) + (int32_t) h * sub_span) << ALT_FRAC_BITS) + (span >> 1)) / span;
+       if (pres > 32767)
+               pres = 32767;
+       if (pres < 0)
+               pres = 0;
+       return (int16_t) pres;
+}
+#endif
+
+#if 0
+int16_t
+ao_temp_to_dC(int16_t temp) __reentrant
+{
+       int16_t ret;
+
+       /* Output voltage at 0°C = 0.755V
+        * Coefficient = 0.00247V/°C
+        * Reference voltage = 1.25V
+        *
+        * temp = ((value / 32767) * 1.25 - 0.755) / 0.00247
+        *      = (value - 19791.268) / 32768 * 1.25 / 0.00247
+        *      ≃ (value - 19791) * 1012 / 65536
+        */
+       ret = ((temp - 19791) * 1012L) >> 16;
+       return ret;
+}
+#endif
diff --git a/src/core/ao_convert_pa.c b/src/core/ao_convert_pa.c
new file mode 100644 (file)
index 0000000..0c93cae
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+#if !defined(AO_CONVERT_TEST) && !defined(AO_FLIGHT_TEST)
+#include "ao.h"
+#endif
+
+static const int32_t altitude_table[] = {
+#include "altitude-pa.h"
+};
+
+#define ALT_SCALE      (1 << ALT_SHIFT)
+#define ALT_MASK       (ALT_SCALE - 1)
+
+int32_t
+ao_pa_to_altitude(int32_t pa)
+{
+       int16_t o;
+       int16_t part;
+       int32_t low, high;
+
+       if (pa < 0)
+               pa = 0;
+       if (pa > 120000)
+               pa = 120000;
+       o = pa >> ALT_SHIFT;
+       part = pa & ALT_MASK;
+
+       low = (int32_t) altitude_table[o] * (ALT_SCALE - part);
+       high = (int32_t) altitude_table[o+1] * part + (ALT_SCALE >> 1);
+       return (low + high) >> ALT_SHIFT;
+}
+
+int32_t
+ao_altitude_to_pa(int32_t alt)
+{
+       int32_t         span, sub_span;
+       uint16_t        l, h, m;
+       int32_t         pa;
+
+       l = 0;
+       h = NALT - 1;
+       while ((h - l) != 1) {
+               m = (l + h) >> 1;
+               if (altitude_table[m] < alt)
+                       h = m;
+               else
+                       l = m;
+       }
+       span = altitude_table[l] - altitude_table[h];
+       sub_span = altitude_table[l] - alt;
+       pa = ((((int32_t) l * (span - sub_span) + (int32_t) h * sub_span) << ALT_SHIFT) + (span >> 1)) / span;
+       if (pa > 120000)
+               pa = 120000;
+       if (pa < 0)
+               pa = 0;
+       return pa;
+}
diff --git a/src/core/ao_convert_pa_test.c b/src/core/ao_convert_pa_test.c
new file mode 100644 (file)
index 0000000..972a4d4
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <stdint.h>
+#define AO_CONVERT_TEST
+#include "ao_host.h"
+#include "ao_convert_pa.c"
+
+#define STEP_P 1
+#define STEP_A 1
+
+static inline i_abs(int i) { return i < 0 ? -i : i; }
+
+main ()
+{
+       int     i;
+       int32_t p_to_a, p_to_a_to_p;
+       int32_t a_to_p, a_to_p_to_a;
+       int max_p_error = 0, max_p_error_p = -1;
+       int max_a_error = 0, max_a_error_a = -1;
+       int p_error;
+       int a_error;
+       int ret = 0;
+
+       for (i = 0; i < 120000 + STEP_P; i += STEP_P) {
+               if (i > 120000)
+                       i = 120000;
+               p_to_a = ao_pa_to_altitude(i);
+               p_to_a_to_p = ao_altitude_to_pa(p_to_a);
+               p_error = i_abs(p_to_a_to_p - i);
+               if (p_error > max_p_error) {
+                       max_p_error = p_error;
+                       max_p_error_p = i;
+               }
+//             printf ("pa %d alt %d pa %d\n",
+//                     i, p_to_a, p_to_a_to_p);
+       }
+       for (i = -1450; i < 74250 + STEP_A; i += STEP_A) {
+               if (i > 74250)
+                       i = 74250;
+               a_to_p = ao_altitude_to_pa(i);
+               a_to_p_to_a = ao_pa_to_altitude(a_to_p);
+               a_error = i_abs(a_to_p_to_a - i);
+               if (a_error > max_a_error) {
+                       max_a_error = a_error;
+                       max_a_error_a = i;
+               }
+//             printf ("alt %d pa %d alt %d\n",
+//                     i, a_to_p, a_to_p_to_a);
+       }
+       if (max_p_error > 2) {
+               printf ("max p error %d at %d\n", max_p_error,
+                       max_p_error_p);
+               ret++;
+       }
+       if (max_a_error > 1) {
+               printf ("max a error %d at %d\n", max_a_error,
+                       max_a_error_a);
+               ret++;
+       }
+       return ret;
+}
diff --git a/src/core/ao_convert_test.c b/src/core/ao_convert_test.c
new file mode 100644 (file)
index 0000000..87e7684
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#include <stdint.h>
+#define AO_CONVERT_TEST
+#define AO_NEED_ALTITUDE_TO_PRES 1
+#include "ao_host.h"
+#include "ao_convert.c"
+
+#define STEP   1
+
+static inline int i_abs(int i) { return i < 0 ? -i : i; }
+
+int main (int argc, char **argv)
+{
+       int     i;
+       int16_t p_to_a, p_to_a_to_p;
+       int16_t a_to_p, a_to_p_to_a;
+       int max_p_error = 0, max_p_error_p = -1;
+       int max_a_error = 0, max_a_error_a = -1;
+       int p_error;
+       int a_error;
+       int ret = 0;
+
+       for (i = 0; i < 32767 + STEP; i += STEP) {
+               if (i > 32767)
+                       i = 32767;
+               p_to_a = ao_pres_to_altitude(i);
+               p_to_a_to_p = ao_altitude_to_pres(p_to_a);
+               p_error = i_abs(p_to_a_to_p - i);
+               if (p_error > max_p_error) {
+                       max_p_error = p_error;
+                       max_p_error_p = i;
+               }
+//             printf ("pres %d alt %d pres %d\n",
+//                     i, p_to_a, p_to_a_to_p);
+       }
+       for (i = -1578; i < 15835 + STEP; i += STEP) {
+               if (i > 15835)
+                       i = 15835;
+               a_to_p = ao_altitude_to_pres(i);
+               a_to_p_to_a = ao_pres_to_altitude(a_to_p);
+               a_error = i_abs(a_to_p_to_a - i);
+               if (a_error > max_a_error) {
+                       max_a_error = a_error;
+                       max_a_error_a = i;
+               }
+//             printf ("alt %d pres %d alt %d\n",
+//                     i, a_to_p, a_to_p_to_a);
+       }
+       if (max_p_error > 2) {
+               printf ("max p error %d at %d\n", max_p_error,
+                       max_p_error_p);
+               ret++;
+       }
+       if (max_a_error > 1) {
+               printf ("max a error %d at %d\n", max_a_error,
+                       max_a_error_a);
+               ret++;
+       }
+       return ret;
+}
diff --git a/src/core/ao_data.h b/src/core/ao_data.h
new file mode 100644 (file)
index 0000000..2b9ef5a
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_DATA_H_
+#define _AO_DATA_H_
+
+#if HAS_ADC
+#define AO_DATA_ADC    (1 << 0)
+#else
+#define AO_DATA_ADC    0
+#endif
+
+#if HAS_MS5607
+#include <ao_ms5607.h>
+#define AO_DATA_MS5607 (1 << 1)
+#else
+#define AO_DATA_MS5607 0
+#endif
+
+#if HAS_MPU6000
+#include <ao_mpu6000.h>
+#define AO_DATA_MPU6000        (1 << 2)
+#else
+#define AO_DATA_MPU6000        0
+#endif
+
+#if HAS_HMC5883
+#include <ao_hmc5883.h>
+#define AO_DATA_HMC5883        (1 << 3)
+#else
+#define AO_DATA_HMC5883        0
+#endif
+
+#if HAS_MMA655X
+#include <ao_mma655x.h>
+#define AO_DATA_MMA655X (1 << 4)
+#else
+#define AO_DATA_MMA655X 0
+#endif
+
+#define AO_DATA_ALL    (AO_DATA_ADC|AO_DATA_MS5607|AO_DATA_MPU6000|AO_DATA_HMC5883|AO_DATA_MMA655X)
+
+struct ao_data {
+       uint16_t                        tick;
+#if HAS_ADC
+       struct ao_adc                   adc;
+#endif
+#if HAS_MS5607
+       struct ao_ms5607_sample         ms5607_raw;
+       struct ao_ms5607_value          ms5607_cooked;
+#endif
+#if HAS_MPU6000
+       struct ao_mpu6000_sample        mpu6000;
+#endif
+#if HAS_HMC5883
+       struct ao_hmc5883_sample        hmc5883;
+#endif
+#if HAS_MMA655X
+       uint16_t                        mma655x;
+#endif
+};
+
+#define ao_data_ring_next(n)   (((n) + 1) & (AO_DATA_RING - 1))
+#define ao_data_ring_prev(n)   (((n) - 1) & (AO_DATA_RING - 1))
+
+extern volatile __xdata struct ao_data ao_data_ring[AO_DATA_RING];
+extern volatile __data uint8_t         ao_data_head;
+extern volatile __data uint8_t         ao_data_present;
+extern volatile __data uint8_t         ao_data_count;
+
+/*
+ * Mark a section of data as ready, check for data complete
+ */
+#define AO_DATA_PRESENT(bit)   do {                                    \
+               if ((ao_data_present |= (bit)) == AO_DATA_ALL) {        \
+                       ao_data_ring[ao_data_head].tick = ao_tick_count; \
+                       ao_data_head = ao_data_ring_next(ao_data_head); \
+                       ao_data_present = 0;                            \
+                       ao_wakeup((void *) &ao_data_head);              \
+               }                                                       \
+       } while (0);
+
+/*
+ * Wait until it is time to write a sensor sample; this is
+ * signaled by the timer tick
+ */
+#define AO_DATA_WAIT() do {                            \
+               ao_sleep((void *) &ao_data_count);      \
+       } while (0)
+
+#if !HAS_BARO && HAS_MS5607
+
+/* Either an MS5607 or an MS5611 hooked to a SPI port
+ */
+
+#define HAS_BARO       1
+
+typedef int32_t        pres_t;
+typedef int32_t alt_t;
+
+#define ao_data_pres_cook(packet)      ao_ms5607_convert(&packet->ms5607_raw, &packet->ms5607_cooked)
+
+#define ao_data_pres(packet)   ((packet)->ms5607_cooked.pres)
+#define ao_data_temp(packet)   ((packet)->ms5607_cooked.temp)
+
+#define pres_to_altitude(p)    ao_pa_to_altitude(p)
+
+#endif
+
+#if !HAS_BARO && HAS_ADC
+
+#define HAS_BARO       1
+
+typedef int16_t pres_t;
+typedef int16_t alt_t;
+
+#define ao_data_pres(packet)   ((packet)->adc.pres)
+#define ao_data_temp(packet)   ((packet)->adc.temp)
+#define pres_to_altitude(p)    ao_pres_to_altitude(p)
+#define ao_data_pres_cook(p)
+
+#endif
+
+/*
+ * Need a few macros to pull data from the sensors:
+ *
+ * ao_data_accel_sample        - pull raw sensor and convert to normalized values
+ * ao_data_accel       - pull normalized value (lives in the same memory)
+ * ao_data_set_accel   - store normalized value back in the sensor location
+ * ao_data_accel_invert        - flip rocket ends for positive acceleration
+ */
+
+#if HAS_ACCEL
+
+/* This section is for an analog accelerometer hooked to one of the ADC pins. As
+ * those are 5V parts, this also requires that the 5V supply be hooked to to anothe ADC
+ * pin so that the both can be measured to correct for changes between the 3.3V and 5V rails
+ */
+
+typedef int16_t accel_t;
+#define ao_data_accel(packet)                  ((packet)->adc.accel)
+#define ao_data_set_accel(packet, a)           ((packet)->adc.accel = (a))
+#define ao_data_accel_invert(a)                        (0x7fff -(a))
+
+/*
+ * Ok, the math here is a bit tricky.
+ *
+ * ao_sample_accel:  ADC output for acceleration
+ * ao_accel_ref:  ADC output for the 5V reference.
+ * ao_cook_accel: Corrected acceleration value
+ * Vcc:           3.3V supply to the CC1111
+ * Vac:           5V supply to the accelerometer
+ * accel:         input voltage to accelerometer ADC pin
+ * ref:           input voltage to 5V reference ADC pin
+ *
+ *
+ * Measured acceleration is ratiometric to Vcc:
+ *
+ *     ao_sample_accel   accel
+ *     ------------ = -----
+ *        32767        Vcc
+ *
+ * Measured 5v reference is also ratiometric to Vcc:
+ *
+ *     ao_accel_ref    ref
+ *     ------------ = -----
+ *        32767        Vcc
+ *
+ *
+ *     ao_accel_ref = 32767 * (ref / Vcc)
+ *
+ * Acceleration is measured ratiometric to the 5V supply,
+ * so what we want is:
+ *
+ *     ao_cook_accel    accel
+ *      ------------- =  -----
+ *          32767         ref
+ *
+ *
+ *                     accel    Vcc
+ *                    = ----- *  ---
+ *                       Vcc     ref
+ *
+ *                      ao_sample_accel       32767
+ *                    = ------------ *  ------------
+ *                         32767        ao_accel_ref
+ *
+ * Multiply through by 32767:
+ *
+ *                      ao_sample_accel * 32767
+ *     ao_cook_accel = --------------------
+ *                          ao_accel_ref
+ *
+ * Now, the tricky part. Getting this to compile efficiently
+ * and keeping all of the values in-range.
+ *
+ * First off, we need to use a shift of 16 instead of * 32767 as SDCC
+ * does the obvious optimizations for byte-granularity shifts:
+ *
+ *     ao_cook_accel = (ao_sample_accel << 16) / ao_accel_ref
+ *
+ * Next, lets check our input ranges:
+ *
+ *     0 <= ao_sample_accel <= 0x7fff          (singled ended ADC conversion)
+ *     0x7000 <= ao_accel_ref <= 0x7fff        (the 5V ref value is close to 0x7fff)
+ *
+ * Plugging in our input ranges, we get an output range of 0 - 0x12490,
+ * which is 17 bits. That won't work. If we take the accel ref and shift
+ * by a bit, we'll change its range:
+ *
+ *     0xe000 <= ao_accel_ref<<1 <= 0xfffe
+ *
+ *     ao_cook_accel = (ao_sample_accel << 16) / (ao_accel_ref << 1)
+ *
+ * Now the output range is 0 - 0x9248, which nicely fits in 16 bits. It
+ * is, however, one bit too large for our signed computations. So, we
+ * take the result and shift that by a bit:
+ *
+ *     ao_cook_accel = ((ao_sample_accel << 16) / (ao_accel_ref << 1)) >> 1
+ *
+ * This finally creates an output range of 0 - 0x4924. As the ADC only
+ * provides 11 bits of data, we haven't actually lost any precision,
+ * just dropped a bit of noise off the low end.
+ */
+
+#if HAS_ACCEL_REF
+
+#define ao_data_accel_cook(packet) \
+       ((uint16_t) ((((uint32_t) (packet)->adc.accel << 16) / ((packet)->adc.accel_ref << 1))) >> 1)
+
+#else
+
+#define ao_data_accel_cook(packet) ((packet)->adc.accel)
+
+#endif /* HAS_ACCEL_REF */
+
+#endif /* HAS_ACCEL */
+
+#if !HAS_ACCEL && HAS_MMA655X
+
+#define HAS_ACCEL      1
+
+typedef int16_t accel_t;
+
+/* MMA655X is hooked up so that positive values represent negative acceleration */
+
+#define ao_data_accel(packet)                  ((packet)->mma655x)
+#define ao_data_accel_cook(packet)             ((packet)->mma655x)
+#define ao_data_set_accel(packet, accel)       ((packet)->mma655x = (accel))
+#define ao_data_accel_invert(accel)            (4095 - (accel))
+
+#endif
+
+#if !HAS_ACCEL && HAS_MPU6000
+
+#define HAS_ACCEL      1
+
+typedef int16_t accel_t;
+
+/* MPU6000 is hooked up so that positive y is positive acceleration */
+#define ao_data_accel(packet)                  ((packet)->mpu6000.accel_y)
+#define ao_data_accel_cook(packet)             (-(packet)->mpu6000.accel_y)
+#define ao_data_set_accel(packet, accel)       ((packet)->mpu6000.accel_y = (accel))
+#define ao_data_accel_invert(a)                        (-(a))
+
+#endif
+
+#endif /* _AO_DATA_H_ */
diff --git a/src/core/ao_dbg.h b/src/core/ao_dbg.h
new file mode 100644 (file)
index 0000000..181e6ec
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_DBG_H_
+#define _AO_DBG_H_
+
+/*
+ * ao_dbg.c
+ *
+ * debug another telemetrum board
+ */
+
+/* Send a byte to the dbg target */
+void
+ao_dbg_send_byte(uint8_t byte);
+
+/* Receive a byte from the dbg target */
+uint8_t
+ao_dbg_recv_byte(void);
+
+/* Start a bulk transfer to/from dbg target memory */
+void
+ao_dbg_start_transfer(uint16_t addr);
+
+/* End a bulk transfer to/from dbg target memory */
+void
+ao_dbg_end_transfer(void);
+
+/* Write a byte to dbg target memory */
+void
+ao_dbg_write_byte(uint8_t byte);
+
+/* Read a byte from dbg target memory */
+uint8_t
+ao_dbg_read_byte(void);
+
+/* Enable dbg mode, switching use of the pins */
+void
+ao_dbg_debug_mode(void);
+
+/* Reset the dbg target */
+void
+ao_dbg_reset(void);
+
+void
+ao_dbg_init(void);
+
+#endif /* _AO_DBG_H_ */
diff --git a/src/core/ao_ee_fake.c b/src/core/ao_ee_fake.c
new file mode 100644 (file)
index 0000000..7fcfcab
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/*
+ * For hardware without eeprom, the config code still
+ * wants to call these functions
+ */
+uint8_t
+ao_ee_write_config(uint8_t *buf, uint16_t len) __reentrant
+{
+       (void) buf;
+       (void) len;
+       return 1;
+}
+
+uint8_t
+ao_ee_read_config(uint8_t *buf, uint16_t len) __reentrant
+{
+       ao_xmemset(buf, '\0', len);
+       return 1;
+}
diff --git a/src/core/ao_fec.h b/src/core/ao_fec.h
new file mode 100644 (file)
index 0000000..eedea8f
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FEC_H_
+#define _AO_FEC_H_
+
+#include <stdint.h>
+
+#define AO_FEC_CRC_INIT                        0xffff
+#define AO_FEC_TRELLIS_TERMINATOR      0x0b
+#define AO_FEC_PREPARE_EXTRA           4
+
+extern const uint8_t ao_fec_whiten_table[];
+
+#if AO_FEC_DEBUG
+void
+ao_fec_dump_bytes(const uint8_t *bytes, uint16_t len, const char *name);
+#endif
+
+static uint16_t inline
+ao_fec_crc_byte(uint8_t byte, uint16_t crc)
+{
+       uint8_t bit;
+
+       for (bit = 0; bit < 8; bit++) {
+               if (((crc & 0x8000) >> 8) ^ (byte & 0x80))
+                       crc = (crc << 1) ^ 0x8005;
+               else
+                       crc = (crc << 1);
+               byte <<= 1;
+       }
+       return crc;
+}
+
+uint16_t
+ao_fec_crc(const uint8_t *bytes, uint8_t len);
+
+/*
+ * 'len' is the length of the original data; 'bytes'
+ * must  be four bytes longer than that, and the first
+ * two after 'len' must be the received crc
+ */
+uint8_t
+ao_fec_check_crc(const uint8_t *bytes, uint8_t len);
+
+/*
+ * Compute CRC, whiten, convolve and interleave data. 'out' must be (len + 4) * 2 bytes long
+ */
+uint8_t
+ao_fec_encode(const uint8_t *in, uint8_t len, uint8_t *out);
+
+/*
+ * Decode data. 'in' is one byte per bit, soft decision
+ * 'out' must be len/8 bytes long
+ */
+
+#define AO_FEC_DECODE_BLOCK    (32)    /* callback must return multiples of this many bits */
+
+#define AO_FEC_DECODE_CRC_OK   0x80    /* stored in out[out_len-1] */
+
+uint8_t
+ao_fec_decode(const uint8_t *in, uint16_t in_len, uint8_t *out, uint8_t out_len, uint16_t (*callback)(void));
+
+/*
+ * Interleave data packed in bytes. 'out' must be 'len' bytes long.
+ */
+uint16_t
+ao_fec_interleave_bytes(uint8_t *in, uint16_t len, uint8_t *out);
+
+#endif /* _AO_FEC_H_ */
diff --git a/src/core/ao_fec_rx.c b/src/core/ao_fec_rx.c
new file mode 100644 (file)
index 0000000..072a9e9
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao_fec.h>
+#include <stdio.h>
+
+#ifdef MEGAMETRUM
+#include <ao.h>
+#endif
+
+#if AO_PROFILE
+#include <ao_profile.h>
+
+uint32_t       ao_fec_decode_start, ao_fec_decode_end;
+#endif
+
+/* 
+ * byte order repeats through 3 2 1 0
+ *     
+ * bit-pair order repeats through
+ *
+ *  1/0 3/2 5/4 7/6
+ *
+ * So, the over all order is:
+ *
+ *     3,1/0   2,1/0   1,1/0   0,1/0
+ *     3,3/2   2,3/2   1,3/2   0,3/2
+ *     3,5/4   2,5/4   1,5/4   0,5/4
+ *     3,7/6   2,7/6   1,7/6   0,7/6
+ *
+ * The raw bit order is thus
+ *
+ *     1e/1f   16/17   0e/0f   06/07
+ *     1c/1d   14/15   0c/0d   04/05
+ *     1a/1b   12/13   0a/0b   02/03
+ *     18/19   10/11   08/09   00/01
+ */
+
+static const uint8_t ao_interleave_order[] = {
+       0x1e, 0x16, 0x0e, 0x06,
+       0x1c, 0x14, 0x0c, 0x04,
+       0x1a, 0x12, 0x0a, 0x02,
+       0x18, 0x10, 0x08, 0x00
+};
+
+static inline uint16_t ao_interleave_index(uint16_t i) {
+       return (i & ~0x1e) | ao_interleave_order[(i & 0x1e) >> 1];
+}
+
+#define NUM_STATE      8
+#define NUM_HIST       24
+
+typedef uint32_t       bits_t;
+
+#define V_0            0xff
+#define V_1            0x00
+
+/*
+ * These are just the 'zero' states; the 'one' states mirror them
+ */
+static const uint8_t ao_fec_decode_table[NUM_STATE*2] = {
+       V_0, V_0,       /* 000 */
+       V_0, V_1,       /* 001 */
+       V_1, V_1,       /* 010 */
+       V_1, V_0,       /* 011 */
+       V_1, V_1,       /* 100 */
+       V_1, V_0,       /* 101 */
+       V_0, V_0,       /* 110 */
+       V_0, V_1        /* 111 */
+};
+
+static inline uint8_t
+ao_next_state(uint8_t state, uint8_t bit)
+{
+       return ((state << 1) | bit) & 0x7;
+}
+
+/*
+ * 'in' is 8-bits per symbol soft decision data
+ * 'len' is input byte length. 'out' must be
+ * 'len'/16 bytes long
+ */
+
+uint8_t
+ao_fec_decode(const uint8_t *in, uint16_t len, uint8_t *out, uint8_t out_len, uint16_t (*callback)(void))
+{
+       static uint32_t cost[2][NUM_STATE];             /* path cost */
+       static bits_t   bits[2][NUM_STATE];             /* save bits to quickly output them */
+
+       uint16_t        i;                              /* input byte index */
+       uint16_t        b;                              /* encoded symbol index (bytes/2) */
+       uint16_t        o;                              /* output bit index */
+       uint8_t         p;                              /* previous cost/bits index */
+       uint8_t         n;                              /* next cost/bits index */
+       uint8_t         state;                          /* state index */
+       const uint8_t   *whiten = ao_fec_whiten_table;
+       uint16_t        interleave;                     /* input byte array index */
+       uint8_t         s0, s1;
+       uint16_t        avail;
+       uint16_t        crc = AO_FEC_CRC_INIT;
+#if AO_PROFILE
+       uint32_t        start_tick;
+#endif
+
+       p = 0;
+       for (state = 0; state < NUM_STATE; state++) {
+               cost[0][state] = 0x7fffffff;
+               bits[0][state] = 0;
+       }
+       cost[0][0] = 0;
+
+       if (callback)
+               avail = 0;
+       else
+               avail = len;
+
+#if AO_PROFILE
+       if (!avail) {
+               avail = callback();
+               if (!avail)
+                       return 0;
+       }
+       start_tick = ao_profile_tick();
+#endif
+       o = 0;
+       for (i = 0; i < len; i += 2) {
+               b = i/2;
+               n = p ^ 1;
+
+               if (!avail) {
+                       avail = callback();
+                       if (!avail)
+                               return 0;
+               }
+
+               /* Fetch one pair of input bytes, de-interleaving
+                * the input.
+                */
+               interleave = ao_interleave_index(i);
+               s0 = in[interleave];
+               s1 = in[interleave+1];
+
+               avail -= 2;
+
+               /* Compute path costs and accumulate output bit path
+                * for each state and encoded bit value. Unrolling
+                * this loop is worth about > 30% performance boost.
+                * Decoding 76-byte remote access packets is reduced
+                * from 14.700ms to 9.3ms. Redoing the loop to
+                * directly compare the two pasts for each future state
+                * reduces this down to 5.7ms
+                */
+
+               /* Ok, of course this is tricky, it's optimized.
+                *
+                * First, it's important to realize that we have 8
+                * states representing the combinations of the three
+                * most recent bits from the encoder. Flipping any
+                * of these three bits flips both output bits.
+                *
+                * 'state<<1' represents the target state for a new
+                * bit value of 0. '(state<<1)+1' represents the
+                * target state for a new bit value of 1.
+                *
+                * 'state' is the previous state with an oldest bit
+                * value of 0. 'state + 4' is the previous state with
+                * an oldest bit value of 1. These two states will
+                * either lead to 'state<<1' or '(state<<1)+1', depending
+                * on whether the next encoded bit was a zero or a one.
+                *
+                * m0 and m1 are the cost of coming to 'state<<1' from
+                * one of the two possible previous states 'state' and
+                * 'state + 4'.
+                *
+                * Because we know the expected values of each
+                * received bit are flipped between these two previous
+                * states:
+                * 
+                *      bitcost(state+4) = 510 - bitcost(state)
+                *
+                * With those two total costs in hand, we then pick
+                * the lower as the cost of the 'state<<1', and compute
+                * the path of bits leading to that state.
+                *
+                * Then, do the same for '(state<<1) + 1'. This time,
+                * instead of computing the m0 and m1 values from
+                * scratch, because the only difference is that we're
+                * expecting a one bit instead of a zero bit, we just
+                * flip the bitcost values around to match the
+                * expected transmitted bits with some tricky
+                * arithmetic which is equivalent to:
+                *
+                *      m0 = cost[p][state] + (510 - bitcost);
+                *      m1 = cost[p][state+4] + bitcost
+                *
+                * Then, the lowest cost and bit trace of the new state
+                * is saved.
+                */
+
+#define DO_STATE(state) {                                              \
+                       uint32_t        bitcost;                        \
+                                                                       \
+                       uint32_t        m0;                             \
+                       uint32_t        m1;                             \
+                       uint32_t        bit;                            \
+                                                                       \
+                       bitcost = ((uint32_t) (s0 ^ ao_fec_decode_table[(state<<1)]) + \
+                                  (uint32_t) (s1 ^ ao_fec_decode_table[(state<<1)|1])); \
+                                                                       \
+                       m0 = cost[p][state] + bitcost;                  \
+                       m1 = cost[p][state+4] + (510 - bitcost);        \
+                       bit = m0 > m1;                                  \
+                       cost[n][state<<1] = bit ? m1 : m0;              \
+                       bits[n][state<<1] = (bits[p][state + (bit<<2)] << 1) | (state&1); \
+                                                                       \
+                       m0 -= (bitcost+bitcost-510);                    \
+                       m1 += (bitcost+bitcost-510);                    \
+                       bit = m0 > m1;                                  \
+                       cost[n][(state<<1)+1] = bit ? m1 : m0;          \
+                       bits[n][(state<<1)+1] = (bits[p][state + (bit<<2)] << 1) | (state&1); \
+               }
+
+               DO_STATE(0);
+               DO_STATE(1);
+               DO_STATE(2);
+               DO_STATE(3);
+
+#if 0
+               printf ("bit %3d symbol %2x %2x:", i/2, s0, s1);
+               for (state = 0; state < NUM_STATE; state++) {
+                       printf (" %8u(%08x)", cost[n][state], bits[n][state]);
+               }
+               printf ("\n");
+#endif
+               p = n;
+
+               /* A loop is needed to handle the last output byte. It
+                * won't have any bits of future data to perform full
+                * error correction, but we might as well give the
+                * best possible answer anyways.
+                */
+               while ((b - o) >= (8 + NUM_HIST) || (i + 2 >= len && b > o)) {
+
+                       /* Compute number of bits to the end of the
+                        * last full byte of data. This is generally
+                        * NUM_HIST, unless we've reached
+                        * the end of the input, in which case
+                        * it will be seven.
+                        */
+                       int8_t          dist = b - (o + 8);     /* distance to last ready-for-writing bit */
+                       uint32_t        min_cost;               /* lowest cost */
+                       uint8_t         min_state;              /* lowest cost state */
+                       uint8_t         byte;
+
+                       /* Find the best fit at the current point
+                        * of the decode.
+                        */
+                       min_cost = cost[p][0];
+                       min_state = 0;
+                       for (state = 1; state < NUM_STATE; state++) {
+                               if (cost[p][state] < min_cost) {
+                                       min_cost = cost[p][state];
+                                       min_state = state;
+                               }
+                       }
+
+                       /* The very last byte of data has the very last bit
+                        * of data left in the state value; just smash the
+                        * bits value in place and reset the 'dist' from
+                        * -1 to 0 so that the full byte is read out
+                        */
+                       if (dist < 0) {
+                               bits[p][min_state] = (bits[p][min_state] << 1) | (min_state & 1);
+                               dist = 0;
+                       }
+
+#if 0
+                       printf ("\tbit %3d min_cost %5d old bit %3d old_state %x bits %02x whiten %0x\n",
+                               i/2, min_cost, o + 8, min_state, (bits[p][min_state] >> dist) & 0xff, *whiten);
+#endif
+                       byte = (bits[p][min_state] >> dist) ^ *whiten++;
+                       *out++ = byte;
+                       if (out_len > 2)
+                               crc = ao_fec_crc_byte(byte, crc);
+
+                       if (!--out_len) {
+                               if ((out[-2] == (uint8_t) (crc >> 8)) &&
+                                   out[-1] == (uint8_t) crc)
+                                       out[-1] = AO_FEC_DECODE_CRC_OK;
+                               else
+                                       out[-1] = 0;
+                               out[-2] = 0;
+                               goto done;
+                       }
+                       o += 8;
+               }
+       }
+done:
+#if AO_PROFILE
+       ao_fec_decode_start = start_tick;
+       ao_fec_decode_end = ao_profile_tick();
+#endif
+       return 1;
+}
diff --git a/src/core/ao_fec_tx.c b/src/core/ao_fec_tx.c
new file mode 100644 (file)
index 0000000..4941d74
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao_fec.h>
+#include <stdio.h>
+
+#if AO_FEC_DEBUG
+void
+ao_fec_dump_bytes(const uint8_t *bytes, uint16_t len, const char *name)
+{
+       uint16_t        i;
+
+       printf ("%s (%d):", name, len);
+       for (i = 0; i < len; i++) {
+               if ((i & 7) == 0)
+                       printf ("\n\t%02x:", i);
+               printf(" %02x", bytes[i]);
+       }
+       printf ("\n");
+}
+#endif
+
+uint16_t
+ao_fec_crc(const uint8_t *bytes, uint8_t len)
+{
+       uint16_t        crc = AO_FEC_CRC_INIT;
+
+       while (len--)
+               crc = ao_fec_crc_byte(*bytes++, crc);
+       return crc;
+}
+
+/*
+ * len is the length of the data; the crc will be
+ * the fist two bytes after that
+ */
+
+uint8_t
+ao_fec_check_crc(const uint8_t *bytes, uint8_t len)
+{
+       uint16_t        computed_crc = ao_fec_crc(bytes, len);
+       uint16_t        received_crc = (bytes[len] << 8) | (bytes[len+1]);
+
+       return computed_crc == received_crc;
+}
+
+/*
+ * Compute CRC and trellis-terminator/interleave-pad bytes
+ */
+static uint8_t
+ao_fec_prepare(const uint8_t *in, uint8_t len, uint8_t *extra)
+{
+       uint16_t        crc = ao_fec_crc (in, len);
+       uint8_t         i = 0;
+       uint8_t         num_fec;
+
+       /* Append CRC */
+       extra[i++] = crc >> 8;
+       extra[i++] = crc;
+
+       /* Append FEC -- 1 byte if odd, two bytes if even */
+       num_fec = 2 - (i & 1);
+       while (num_fec--)
+               extra[i++] = AO_FEC_TRELLIS_TERMINATOR;
+       return i;
+}
+
+const uint8_t ao_fec_whiten_table[] = {
+#include "ao_whiten.h"
+};
+
+static const uint8_t ao_fec_encode_table[16] = {
+/* next 0  1     state */
+       0, 3,   /* 000 */
+       1, 2,   /* 001 */
+       3, 0,   /* 010 */
+       2, 1,   /* 011 */
+       3, 0,   /* 100 */
+       2, 1,   /* 101 */
+       0, 3,   /* 110 */
+       1, 2    /* 111 */
+};
+
+uint8_t
+ao_fec_encode(const uint8_t *in, uint8_t len, uint8_t *out)
+{
+       uint8_t         extra[AO_FEC_PREPARE_EXTRA];
+       uint8_t         extra_len;
+       uint32_t        encode, interleave;
+       uint8_t         pair, byte, bit;
+       uint16_t        fec = 0;
+       const uint8_t   *whiten = ao_fec_whiten_table;
+
+       extra_len = ao_fec_prepare(in, len, extra);
+       for (pair = 0; pair < len + extra_len; pair += 2) {
+               encode = 0;
+               for (byte = 0; byte < 2; byte++) {
+                       if (pair + byte == len)
+                               in = extra;
+                       fec |= *in++ ^ *whiten++;
+                       for (bit = 0; bit < 8; bit++) {
+                               encode = encode << 2 | ao_fec_encode_table[fec >> 7];
+                               fec = (fec << 1) & 0x7ff;
+                       }
+               }
+
+               interleave = 0;
+               for (bit = 0; bit < 4 * 4; bit++) {
+                       uint8_t byte_shift = (bit & 0x3) << 3;
+                       uint8_t bit_shift = (bit & 0xc) >> 1;
+
+                       interleave = (interleave << 2) | ((encode >> (byte_shift + bit_shift)) & 0x3);
+               }
+               *out++ = interleave >> 24;
+               *out++ = interleave >> 16;
+               *out++ = interleave >> 8;
+               *out++ = interleave >> 0;
+       }
+       return (len + extra_len) * 2;
+}
diff --git a/src/core/ao_flight.c b/src/core/ao_flight.c
new file mode 100644 (file)
index 0000000..aa4f696
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#include <ao_log.h>
+#endif
+
+#ifndef HAS_ACCEL
+#error Please define HAS_ACCEL
+#endif
+
+#ifndef HAS_GPS
+#error Please define HAS_GPS
+#endif
+
+#ifndef HAS_USB
+#error Please define HAS_USB
+#endif
+
+#ifndef HAS_TELEMETRY
+#define HAS_TELEMETRY  HAS_RADIO
+#endif
+
+/* Main flight thread. */
+
+__pdata enum ao_flight_state   ao_flight_state;        /* current flight state */
+__pdata uint16_t               ao_boost_tick;          /* time of launch detect */
+__pdata uint16_t               ao_motor_number;        /* number of motors burned so far */
+
+/*
+ * track min/max data over a long interval to detect
+ * resting
+ */
+static __data uint16_t         ao_interval_end;
+static __data int16_t          ao_interval_min_height;
+static __data int16_t          ao_interval_max_height;
+#if HAS_ACCEL
+static __data int16_t          ao_coast_avg_accel;
+#endif
+
+__pdata uint8_t                        ao_flight_force_idle;
+
+/* We also have a clock, which can be used to sanity check things in
+ * case of other failures
+ */
+
+#define BOOST_TICKS_MAX        AO_SEC_TO_TICKS(15)
+
+/* Landing is detected by getting constant readings from both pressure and accelerometer
+ * for a fairly long time (AO_INTERVAL_TICKS)
+ */
+#define AO_INTERVAL_TICKS      AO_SEC_TO_TICKS(10)
+
+#define abs(a) ((a) < 0 ? -(a) : (a))
+
+void
+ao_flight(void)
+{
+       ao_sample_init();
+       ao_flight_state = ao_flight_startup;
+       for (;;) {
+
+               /*
+                * Process ADC samples, just looping
+                * until the sensors are calibrated.
+                */
+               if (!ao_sample())
+                       continue;
+
+               switch (ao_flight_state) {
+               case ao_flight_startup:
+
+                       /* Check to see what mode we should go to.
+                        *  - Invalid mode if accel cal appears to be out
+                        *  - pad mode if we're upright,
+                        *  - idle mode otherwise
+                        */
+#if HAS_ACCEL
+                       if (ao_config.accel_plus_g == 0 ||
+                           ao_config.accel_minus_g == 0 ||
+                           ao_ground_accel < ao_config.accel_plus_g - ACCEL_NOSE_UP ||
+                           ao_ground_accel > ao_config.accel_minus_g + ACCEL_NOSE_UP)
+                       {
+                               /* Detected an accel value outside -1.5g to 1.5g
+                                * (or uncalibrated values), so we go into invalid mode
+                                */
+                               ao_flight_state = ao_flight_invalid;
+
+#if HAS_RADIO && PACKET_HAS_SLAVE
+                               /* Turn on packet system in invalid mode on TeleMetrum */
+                               ao_packet_slave_start();
+#endif
+                       } else
+#endif
+                               if (!ao_flight_force_idle
+#if HAS_ACCEL
+                                   && ao_ground_accel < ao_config.accel_plus_g + ACCEL_NOSE_UP
+#endif
+                                       )
+                       {
+                               /* Set pad mode - we can fly! */
+                               ao_flight_state = ao_flight_pad;
+#if HAS_USB && HAS_RADIO && !HAS_FLIGHT_DEBUG
+                               /* Disable the USB controller in flight mode
+                                * to save power
+                                */
+                               ao_usb_disable();
+#endif
+
+#if !HAS_ACCEL
+                               /* Disable packet mode in pad state on TeleMini */
+                               ao_packet_slave_stop();
+#endif
+
+#if HAS_TELEMETRY
+                               /* Turn on telemetry system */
+                               ao_rdf_set(1);
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_PAD);
+#endif
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+                       } else {
+                               /* Set idle mode */
+                               ao_flight_state = ao_flight_idle;
+#if HAS_ACCEL && HAS_RADIO && PACKET_HAS_SLAVE
+                               /* Turn on packet system in idle mode on TeleMetrum */
+                               ao_packet_slave_start();
+#endif
+
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+                       }
+                       /* wakeup threads due to state change */
+                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+
+                       break;
+               case ao_flight_pad:
+
+                       /* pad to boost:
+                        *
+                        * barometer: > 20m vertical motion
+                        *             OR
+                        * accelerometer: > 2g AND velocity > 5m/s
+                        *
+                        * The accelerometer should always detect motion before
+                        * the barometer, but we use both to make sure this
+                        * transition is detected. If the device
+                        * doesn't have an accelerometer, then ignore the
+                        * speed and acceleration as they are quite noisy
+                        * on the pad.
+                        */
+                       if (ao_height > AO_M_TO_HEIGHT(20)
+#if HAS_ACCEL
+                           || (ao_accel > AO_MSS_TO_ACCEL(20) &&
+                               ao_speed > AO_MS_TO_SPEED(5))
+#endif
+                               )
+                       {
+                               ao_flight_state = ao_flight_boost;
+                               ao_boost_tick = ao_sample_tick;
+
+                               /* start logging data */
+                               ao_log_start();
+
+#if HAS_TELEMETRY
+                               /* Increase telemetry rate */
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_FLIGHT);
+
+                               /* disable RDF beacon */
+                               ao_rdf_set(0);
+#endif
+
+#if HAS_GPS
+                               /* Record current GPS position by waking up GPS log tasks */
+                               ao_wakeup(&ao_gps_data);
+                               ao_wakeup(&ao_gps_tracking_data);
+#endif
+
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+               case ao_flight_boost:
+
+                       /* boost to fast:
+                        *
+                        * accelerometer: start to fall at > 1/4 G
+                        *              OR
+                        * time: boost for more than 15 seconds
+                        *
+                        * Detects motor burn out by the switch from acceleration to
+                        * deceleration, or by waiting until the maximum burn duration
+                        * (15 seconds) has past.
+                        */
+                       if ((ao_accel < AO_MSS_TO_ACCEL(-2.5) && ao_height > AO_M_TO_HEIGHT(100)) ||
+                           (int16_t) (ao_sample_tick - ao_boost_tick) > BOOST_TICKS_MAX)
+                       {
+#if HAS_ACCEL
+                               ao_flight_state = ao_flight_fast;
+                               ao_coast_avg_accel = ao_accel;
+#else
+                               ao_flight_state = ao_flight_coast;
+#endif
+                               ++ao_motor_number;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+#if HAS_ACCEL
+               case ao_flight_fast:
+                       /*
+                        * This is essentially the same as coast,
+                        * but the barometer is being ignored as
+                        * it may be unreliable.
+                        */
+                       if (ao_speed < AO_MS_TO_SPEED(AO_MAX_BARO_SPEED))
+                       {
+                               ao_flight_state = ao_flight_coast;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       } else
+                               goto check_re_boost;
+                       break;
+#endif
+               case ao_flight_coast:
+
+                       /*
+                        * By customer request - allow the user
+                        * to lock out apogee detection for a specified
+                        * number of seconds.
+                        */
+                       if (ao_config.apogee_lockout) {
+                               if ((ao_sample_tick - ao_boost_tick) <
+                                   AO_SEC_TO_TICKS(ao_config.apogee_lockout))
+                                       break;
+                       }
+
+                       /* apogee detect: coast to drogue deploy:
+                        *
+                        * speed: < 0
+                        *
+                        * Also make sure the model altitude is tracking
+                        * the measured altitude reasonably closely; otherwise
+                        * we're probably transsonic.
+                        */
+                       if (ao_speed < 0
+#if !HAS_ACCEL
+                           && (ao_sample_alt >= AO_MAX_BARO_HEIGHT || ao_error_h_sq_avg < 100)
+#endif
+                               )
+                       {
+#if HAS_IGNITE
+                               /* ignite the drogue charge */
+                               ao_ignite(ao_igniter_drogue);
+#endif
+
+#if HAS_TELEMETRY
+                               /* slow down the telemetry system */
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_RECOVER);
+
+                               /* Turn the RDF beacon back on */
+                               ao_rdf_set(1);
+#endif
+
+                               /* and enter drogue state */
+                               ao_flight_state = ao_flight_drogue;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+#if HAS_ACCEL
+                       else {
+                       check_re_boost:
+                               ao_coast_avg_accel = ao_coast_avg_accel - (ao_coast_avg_accel >> 6) + (ao_accel >> 6);
+                               if (ao_coast_avg_accel > AO_MSS_TO_ACCEL(20)) {
+                                       ao_boost_tick = ao_sample_tick;
+                                       ao_flight_state = ao_flight_boost;
+                                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                               }
+                       }
+#endif
+
+                       break;
+               case ao_flight_drogue:
+
+                       /* drogue to main deploy:
+                        *
+                        * barometer: reach main deploy altitude
+                        *
+                        * Would like to use the accelerometer for this test, but
+                        * the orientation of the flight computer is unknown after
+                        * drogue deploy, so we ignore it. Could also detect
+                        * high descent rate using the pressure sensor to
+                        * recognize drogue deploy failure and eject the main
+                        * at that point. Perhaps also use the drogue sense lines
+                        * to notice continutity?
+                        */
+                       if (ao_height <= ao_config.main_deploy)
+                       {
+#if HAS_IGNITE
+                               ao_ignite(ao_igniter_main);
+#endif
+
+                               /*
+                                * Start recording min/max height
+                                * to figure out when the rocket has landed
+                                */
+
+                               /* initialize interval values */
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
+
+                               ao_interval_min_height = ao_interval_max_height = ao_avg_height;
+
+                               ao_flight_state = ao_flight_main;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+
+                       /* fall through... */
+               case ao_flight_main:
+
+                       /* main to land:
+                        *
+                        * barometer: altitude stable
+                        */
+
+                       if (ao_avg_height < ao_interval_min_height)
+                               ao_interval_min_height = ao_avg_height;
+                       if (ao_avg_height > ao_interval_max_height)
+                               ao_interval_max_height = ao_avg_height;
+
+                       if ((int16_t) (ao_sample_tick - ao_interval_end) >= 0) {
+                               if (ao_interval_max_height - ao_interval_min_height <= AO_M_TO_HEIGHT(4))
+                               {
+                                       ao_flight_state = ao_flight_landed;
+
+                                       /* turn off the ADC capture */
+                                       ao_timer_set_adc_interval(0);
+
+                                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                               }
+                               ao_interval_min_height = ao_interval_max_height = ao_avg_height;
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
+                       }
+                       break;
+               default:
+                       break;
+               }
+       }
+}
+
+#if HAS_FLIGHT_DEBUG
+static inline int int_part(int16_t i)  { return i >> 4; }
+static inline int frac_part(int16_t i) { return ((i & 0xf) * 100 + 8) / 16; }
+
+static void
+ao_flight_dump(void)
+{
+#if HAS_ACCEL
+       int16_t accel;
+
+       accel = ((ao_ground_accel - ao_sample_accel) * ao_accel_scale) >> 16;
+#endif
+
+       printf ("sample:\n");
+       printf ("  tick        %d\n", ao_sample_tick);
+       printf ("  raw pres    %d\n", ao_sample_pres);
+#if HAS_ACCEL
+       printf ("  raw accel   %d\n", ao_sample_accel);
+#endif
+       printf ("  ground pres %d\n", ao_ground_pres);
+       printf ("  ground alt  %d\n", ao_ground_height);
+#if HAS_ACCEL
+       printf ("  raw accel   %d\n", ao_sample_accel);
+       printf ("  groundaccel %d\n", ao_ground_accel);
+       printf ("  accel_2g    %d\n", ao_accel_2g);
+#endif
+
+       printf ("  alt         %d\n", ao_sample_alt);
+       printf ("  height      %d\n", ao_sample_height);
+#if HAS_ACCEL
+       printf ("  accel       %d.%02d\n", int_part(accel), frac_part(accel));
+#endif
+
+
+       printf ("kalman:\n");
+       printf ("  height      %d\n", ao_height);
+       printf ("  speed       %d.%02d\n", int_part(ao_speed), frac_part(ao_speed));
+       printf ("  accel       %d.%02d\n", int_part(ao_accel), frac_part(ao_accel));
+       printf ("  max_height  %d\n", ao_max_height);
+       printf ("  avg_height  %d\n", ao_avg_height);
+       printf ("  error_h     %d\n", ao_error_h);
+       printf ("  error_avg   %d\n", ao_error_h_sq_avg);
+}
+
+__code struct ao_cmds ao_flight_cmds[] = {
+       { ao_flight_dump,       "F\0Dump flight status" },
+       { 0, NULL },
+};
+#endif
+
+static __xdata struct ao_task  flight_task;
+
+void
+ao_flight_init(void)
+{
+       ao_flight_state = ao_flight_startup;
+#if HAS_FLIGHT_DEBUG
+       ao_cmd_register(&ao_flight_cmds[0]);
+#endif
+       ao_add_task(&flight_task, ao_flight, "flight");
+}
diff --git a/src/core/ao_flight.h b/src/core/ao_flight.h
new file mode 100644 (file)
index 0000000..b80202f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_FLIGHT_H_
+#define _AO_FLIGHT_H_
+
+
+/*
+ * ao_flight.c
+ */
+
+enum ao_flight_state {
+       ao_flight_startup = 0,
+       ao_flight_idle = 1,
+       ao_flight_pad = 2,
+       ao_flight_boost = 3,
+       ao_flight_fast = 4,
+       ao_flight_coast = 5,
+       ao_flight_drogue = 6,
+       ao_flight_main = 7,
+       ao_flight_landed = 8,
+       ao_flight_invalid = 9
+};
+
+extern __pdata enum ao_flight_state    ao_flight_state;
+extern __pdata uint16_t                        ao_boost_tick;
+extern __pdata uint16_t                        ao_motor_number;
+
+extern __pdata uint16_t                        ao_launch_time;
+extern __pdata uint8_t                 ao_flight_force_idle;
+
+/* Flight thread */
+void
+ao_flight(void);
+
+/* Initialize flight thread */
+void
+ao_flight_init(void);
+
+/*
+ * ao_flight_nano.c
+ */
+
+void
+ao_flight_nano_init(void);
+
+#endif /* _AO_FLIGHT_H_ */
diff --git a/src/core/ao_flight_nano.c b/src/core/ao_flight_nano.c
new file mode 100644 (file)
index 0000000..406d81a
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/* Main flight thread. */
+
+__pdata enum ao_flight_state   ao_flight_state;        /* current flight state */
+__pdata uint16_t               ao_launch_tick;         /* time of launch detect */
+
+/*
+ * track min/max data over a long interval to detect
+ * resting
+ */
+__pdata uint16_t               ao_interval_end;
+__pdata alt_t                  ao_interval_min_height;
+__pdata alt_t                  ao_interval_max_height;
+
+__pdata uint8_t                        ao_flight_force_idle;
+
+/* Landing is detected by getting constant readings from both pressure and accelerometer
+ * for a fairly long time (AO_INTERVAL_TICKS)
+ */
+#define AO_INTERVAL_TICKS      AO_SEC_TO_TICKS(5)
+
+static void
+ao_flight_nano(void)
+{
+       ao_sample_init();
+       ao_flight_state = ao_flight_startup;
+
+       for (;;) {
+               /*
+                * Process ADC samples, just looping
+                * until the sensors are calibrated.
+                */
+               if (!ao_sample())
+                       continue;
+
+               switch (ao_flight_state) {
+               case ao_flight_startup:
+                       if (ao_flight_force_idle) {
+                               /* Set idle mode */
+                               ao_flight_state = ao_flight_idle;
+                       } else {
+                               ao_flight_state = ao_flight_pad;
+                               /* Disable packet mode in pad state */
+                               ao_packet_slave_stop();
+
+                               /* Turn on telemetry system */
+                               ao_rdf_set(1);
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_PAD);
+                       }
+                       /* signal successful initialization by turning off the LED */
+                       ao_led_off(AO_LED_RED);
+
+                       /* wakeup threads due to state change */
+                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       break;
+               case ao_flight_pad:
+                       if (ao_height> AO_M_TO_HEIGHT(20)) {
+                               ao_flight_state = ao_flight_drogue;
+                               ao_launch_tick = ao_sample_tick;
+
+                               /* start logging data */
+                               ao_log_start();
+
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+               case ao_flight_drogue:
+                       /* drogue/main to land:
+                        *
+                        * barometer: altitude stable
+                        */
+
+                       if (ao_height < ao_interval_min_height)
+                               ao_interval_min_height = ao_height;
+                       if (ao_height > ao_interval_max_height)
+                               ao_interval_max_height = ao_height;
+
+                       if ((int16_t) (ao_sample_tick - ao_interval_end) >= 0) {
+                               if (ao_interval_max_height - ao_interval_min_height < AO_M_TO_HEIGHT(5))
+                               {
+                                       ao_flight_state = ao_flight_landed;
+
+                                       /* turn off the ADC capture */
+                                       ao_timer_set_adc_interval(0);
+                                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                               }
+                               ao_interval_min_height = ao_interval_max_height = ao_height;
+                               ao_interval_end = ao_sample_tick + AO_INTERVAL_TICKS;
+                       }
+                       break;
+               }
+       }
+}
+
+static __xdata struct ao_task  flight_task;
+
+void
+ao_flight_nano_init(void)
+{
+       ao_flight_state = ao_flight_startup;
+       ao_add_task(&flight_task, ao_flight_nano, "flight");
+}
diff --git a/src/core/ao_freq.c b/src/core/ao_freq.c
new file mode 100644 (file)
index 0000000..12496f6
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+/*
+ * The provided 'calibration' value is
+ * that needed to tune the radio to precisely 434550kHz.
+ * Use that to 'walk' to the target frequency by following
+ * a 'bresenham' line from 434550kHz to the target
+ * frequency, and updating the radio setting along the way
+ */
+
+int32_t ao_freq_to_set(int32_t freq, int32_t cal) __reentrant
+{
+       static __pdata int32_t  set;
+       static __pdata uint8_t  neg;
+       static __pdata int32_t  error;
+
+       set = 0;
+       neg = 0;
+       error = -434550 / 2;
+
+       if ((freq -= 434550) < 0) {
+               neg = 1;
+               freq = -freq;
+       }
+       for (;;) {
+               if (error > 0) {
+                       error -= 434550;
+                       set++;
+               } else {
+                       error += cal;
+                       if (--freq < 0)
+                               break;
+               }
+       }
+       if (neg)
+               set = -set;
+       return cal + set;
+}
diff --git a/src/core/ao_gps_print.c b/src/core/ao_gps_print.c
new file mode 100644 (file)
index 0000000..47c945d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_GPS_TEST
+#include "ao.h"
+#endif
+#include "ao_telem.h"
+
+void
+ao_gps_print(__xdata struct ao_gps_orig *gps_data) __reentrant
+{
+       char    state;
+
+       if (gps_data->flags & AO_GPS_VALID)
+               state = AO_TELEM_GPS_STATE_LOCKED;
+       else if (gps_data->flags & AO_GPS_RUNNING)
+               state = AO_TELEM_GPS_STATE_UNLOCKED;
+       else
+               state = AO_TELEM_GPS_STATE_ERROR;
+       printf(AO_TELEM_GPS_STATE " %c "
+              AO_TELEM_GPS_NUM_SAT " %d ",
+              state,
+              (gps_data->flags & AO_GPS_NUM_SAT_MASK) >> AO_GPS_NUM_SAT_SHIFT);
+       if (!(gps_data->flags & AO_GPS_VALID))
+               return;
+       printf(AO_TELEM_GPS_LATITUDE " %ld "
+              AO_TELEM_GPS_LONGITUDE " %ld "
+              AO_TELEM_GPS_ALTITUDE " %d ",
+              (long) gps_data->latitude,
+              (long) gps_data->longitude,
+              gps_data->altitude);
+
+       if (gps_data->flags & AO_GPS_DATE_VALID)
+               printf(AO_TELEM_GPS_YEAR " %d "
+                      AO_TELEM_GPS_MONTH " %d "
+                      AO_TELEM_GPS_DAY " %d ",
+                      gps_data->year,
+                      gps_data->month,
+                      gps_data->day);
+
+       printf(AO_TELEM_GPS_HOUR " %d "
+              AO_TELEM_GPS_MINUTE " %d "
+              AO_TELEM_GPS_SECOND " %d ",
+              gps_data->hour,
+              gps_data->minute,
+              gps_data->second);
+
+       printf(AO_TELEM_GPS_HDOP " %d ",
+              gps_data->hdop * 2);
+
+       if (gps_data->flags & AO_GPS_COURSE_VALID) {
+               printf(AO_TELEM_GPS_HERROR " %d "
+                      AO_TELEM_GPS_VERROR " %d "
+                      AO_TELEM_GPS_VERTICAL_SPEED " %d "
+                      AO_TELEM_GPS_HORIZONTAL_SPEED " %d "
+                      AO_TELEM_GPS_COURSE " %d ",
+                      gps_data->h_error,
+                      gps_data->v_error,
+                      gps_data->climb_rate,
+                      gps_data->ground_speed,
+                      (int) gps_data->course * 2);
+       }
+}
+
+void
+ao_gps_tracking_print(__xdata struct ao_gps_tracking_orig *gps_tracking_data) __reentrant
+{
+       uint8_t c, n, v;
+       __xdata struct ao_gps_sat_orig  *sat;
+
+       n = gps_tracking_data->channels;
+       if (n == 0)
+               return;
+
+       sat = gps_tracking_data->sats;
+       v = 0;
+       for (c = 0; c < n; c++) {
+               if (sat->svid)
+                       v++;
+               sat++;
+       }
+
+       printf (AO_TELEM_SAT_NUM " %d ",
+               v);
+
+       sat = gps_tracking_data->sats;
+       v = 0;
+       for (c = 0; c < n; c++) {
+               if (sat->svid) {
+                       printf (AO_TELEM_SAT_SVID "%d %d "
+                               AO_TELEM_SAT_C_N_0 "%d %d ",
+                               v, sat->svid,
+                               v, sat->c_n_1);
+                       v++;
+               }
+               sat++;
+       }
+}
diff --git a/src/core/ao_gps_report.c b/src/core/ao_gps_report.c
new file mode 100644 (file)
index 0000000..c52ef62
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+ao_gps_report(void)
+{
+       static __xdata struct ao_log_record             gps_log;
+       static __xdata struct ao_telemetry_location     gps_data;
+       uint8_t date_reported = 0;
+
+       for (;;) {
+               ao_sleep(&ao_gps_data);
+               ao_mutex_get(&ao_gps_mutex);
+               ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
+               ao_mutex_put(&ao_gps_mutex);
+
+               if (!(gps_data.flags & AO_GPS_VALID))
+                       continue;
+
+               gps_log.tick = ao_gps_tick;
+               gps_log.type = AO_LOG_GPS_TIME;
+               gps_log.u.gps_time.hour = gps_data.hour;
+               gps_log.u.gps_time.minute = gps_data.minute;
+               gps_log.u.gps_time.second = gps_data.second;
+               gps_log.u.gps_time.flags = gps_data.flags;
+               ao_log_data(&gps_log);
+               gps_log.type = AO_LOG_GPS_LAT;
+               gps_log.u.gps_latitude = gps_data.latitude;
+               ao_log_data(&gps_log);
+               gps_log.type = AO_LOG_GPS_LON;
+               gps_log.u.gps_longitude = gps_data.longitude;
+               ao_log_data(&gps_log);
+               gps_log.type = AO_LOG_GPS_ALT;
+               gps_log.u.gps_altitude.altitude = gps_data.altitude;
+               gps_log.u.gps_altitude.unused = 0xffff;
+               ao_log_data(&gps_log);
+               if (!date_reported && (gps_data.flags & AO_GPS_DATE_VALID)) {
+                       gps_log.type = AO_LOG_GPS_DATE;
+                       gps_log.u.gps_date.year = gps_data.year;
+                       gps_log.u.gps_date.month = gps_data.month;
+                       gps_log.u.gps_date.day = gps_data.day;
+                       gps_log.u.gps_date.extra = 0;
+                       date_reported = ao_log_data(&gps_log);
+               }
+       }
+}
+
+void
+ao_gps_tracking_report(void)
+{
+       static __xdata struct ao_log_record             gps_log;
+       static __xdata struct ao_telemetry_satellite    gps_tracking_data;
+       uint8_t c, n;
+
+       for (;;) {
+               ao_sleep(&ao_gps_tracking_data);
+               ao_mutex_get(&ao_gps_mutex);
+               gps_log.tick = ao_gps_tick;
+               ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
+               ao_mutex_put(&ao_gps_mutex);
+
+               if (!(n = gps_tracking_data.channels))
+                       continue;
+
+               gps_log.type = AO_LOG_GPS_SAT;
+               for (c = 0; c < n; c++)
+                       if ((gps_log.u.gps_sat.svid = gps_tracking_data.sats[c].svid))
+                       {
+                               gps_log.u.gps_sat.c_n = gps_tracking_data.sats[c].c_n_1;
+                               ao_log_data(&gps_log);
+                       }
+       }
+}
+
+__xdata struct ao_task ao_gps_report_task;
+__xdata struct ao_task ao_gps_tracking_report_task;
+
+void
+ao_gps_report_init(void)
+{
+       ao_add_task(&ao_gps_report_task, ao_gps_report, "gps_report");
+       ao_add_task(&ao_gps_tracking_report_task, ao_gps_tracking_report, "gps_tracking_report");
+}
diff --git a/src/core/ao_gps_report_mega.c b/src/core/ao_gps_report_mega.c
new file mode 100644 (file)
index 0000000..47891ca
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+ao_gps_report_mega(void)
+{
+       static __xdata struct ao_log_mega               gps_log;
+       static __xdata struct ao_telemetry_location     gps_data;
+       uint8_t date_reported = 0;
+
+       for (;;) {
+               ao_sleep(&ao_gps_data);
+               ao_mutex_get(&ao_gps_mutex);
+               ao_xmemcpy(&gps_data, &ao_gps_data, sizeof (ao_gps_data));
+               ao_mutex_put(&ao_gps_mutex);
+
+               if (!(gps_data.flags & AO_GPS_VALID))
+                       continue;
+
+               gps_log.tick = ao_gps_tick;
+               gps_log.type = AO_LOG_GPS_TIME;
+               gps_log.u.gps.latitude = gps_data.latitude;
+               gps_log.u.gps.longitude = gps_data.longitude;
+               gps_log.u.gps.altitude = gps_data.altitude;
+
+               gps_log.u.gps.hour = gps_data.hour;
+               gps_log.u.gps.minute = gps_data.minute;
+               gps_log.u.gps.second = gps_data.second;
+               gps_log.u.gps.flags = gps_data.flags;
+               gps_log.u.gps.year = gps_data.year;
+               gps_log.u.gps.month = gps_data.month;
+               gps_log.u.gps.day = gps_data.day;
+               ao_log_mega(&gps_log);
+       }
+}
+
+void
+ao_gps_tracking_report_mega(void)
+{
+       static __xdata struct ao_log_mega               gps_log;
+       static __xdata struct ao_telemetry_satellite    gps_tracking_data;
+       uint8_t c, n, i;
+
+       for (;;) {
+               ao_sleep(&ao_gps_tracking_data);
+               ao_mutex_get(&ao_gps_mutex);
+               ao_xmemcpy(&gps_tracking_data, &ao_gps_tracking_data, sizeof (ao_gps_tracking_data));
+               ao_mutex_put(&ao_gps_mutex);
+
+               if (!(n = gps_tracking_data.channels))
+                       continue;
+
+               gps_log.type = AO_LOG_GPS_SAT;
+               gps_log.tick = ao_gps_tick;
+               i = 0;
+               for (c = 0; c < n; c++)
+                       if ((gps_log.u.gps_sat.sats[i].svid = gps_tracking_data.sats[c].svid))
+                       {
+                               gps_log.u.gps_sat.sats[i].c_n = gps_tracking_data.sats[c].c_n_1;
+                               i++;
+                       }
+               gps_log.u.gps_sat.channels = i;
+               ao_log_mega(&gps_log);
+       }
+}
+
+__xdata struct ao_task ao_gps_report_mega_task;
+__xdata struct ao_task ao_gps_tracking_report_mega_task;
+
+void
+ao_gps_report_mega_init(void)
+{
+       ao_add_task(&ao_gps_report_mega_task,
+                   ao_gps_report_mega,
+                   "gps_report");
+       ao_add_task(&ao_gps_tracking_report_mega_task,
+                   ao_gps_tracking_report_mega,
+                   "gps_tracking_report");
+}
diff --git a/src/core/ao_host.h b/src/core/ao_host.h
new file mode 100644 (file)
index 0000000..6eb752c
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define _GNU_SOURCE
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define AO_ADC_RING    64
+#define ao_adc_ring_next(n)    (((n) + 1) & (AO_ADC_RING - 1))
+#define ao_adc_ring_prev(n)    (((n) - 1) & (AO_ADC_RING - 1))
+
+/*
+ * One set of samples read from the A/D converter
+ */
+struct ao_adc {
+       uint16_t        tick;           /* tick when the sample was read */
+       int16_t         accel;          /* accelerometer */
+       int16_t         pres;           /* pressure sensor */
+       int16_t         temp;           /* temperature sensor */
+       int16_t         v_batt;         /* battery voltage */
+       int16_t         sense_d;        /* drogue continuity sense */
+       int16_t         sense_m;        /* main continuity sense */
+};
+
+#define __pdata
+#define __data
+#define __xdata
+#define __code
+#define __reentrant
+
+#define DATA_TO_XDATA(a)       (a)
+#define PDATA_TO_XDATA(a)      (a)
+#define CODE_TO_XDATA(a)       (a)
+
+enum ao_flight_state {
+       ao_flight_startup = 0,
+       ao_flight_idle = 1,
+       ao_flight_pad = 2,
+       ao_flight_boost = 3,
+       ao_flight_fast = 4,
+       ao_flight_coast = 5,
+       ao_flight_drogue = 6,
+       ao_flight_main = 7,
+       ao_flight_landed = 8,
+       ao_flight_invalid = 9
+};
+
+struct ao_adc ao_adc_ring[AO_ADC_RING];
+uint8_t ao_adc_head;
+
+#define ao_led_on(l)
+#define ao_led_off(l)
+#define ao_timer_set_adc_interval(i)
+#define ao_wakeup(wchan) ao_dump_state(wchan)
+#define ao_cmd_register(c)
+#define ao_usb_disable()
+#define ao_telemetry_set_interval(x)
+#define ao_delay(x)
+
+enum ao_igniter {
+       ao_igniter_drogue = 0,
+       ao_igniter_main = 1
+};
+
+void
+ao_ignite(enum ao_igniter igniter)
+{
+       printf ("ignite %s\n", igniter == ao_igniter_drogue ? "drogue" : "main");
+}
+
+struct ao_task {
+       int dummy;
+};
+
+#define ao_add_task(t,f,n)
+
+#define ao_log_start()
+#define ao_log_stop()
+
+#define AO_MS_TO_TICKS(ms)     ((ms) / 10)
+#define AO_SEC_TO_TICKS(s)     ((s) * 100)
+
+#define AO_FLIGHT_TEST
+
+struct ao_adc ao_adc_static;
+
+FILE *emulator_in;
+
+void
+ao_dump_state(void *wchan);
+
+void
+ao_sleep(void *wchan);
+
+const char const * const ao_state_names[] = {
+       "startup", "idle", "pad", "boost", "fast",
+       "coast", "drogue", "main", "landed", "invalid"
+};
+
+struct ao_cmds {
+       void            (*func)(void);
+       const char      *help;
+};
+
+
+struct ao_config {
+       uint16_t        main_deploy;
+       int16_t         accel_zero_g;
+};
+
+#define ao_config_get()
+
+struct ao_config ao_config = { 250, 16000 };
+
+#define ao_xmemcpy(d,s,c) memcpy(d,s,c)
+#define ao_xmemset(d,v,c) memset(d,v,c)
+#define ao_xmemcmp(d,s,c) memcmp(d,s,c)
diff --git a/src/core/ao_ignite.c b/src/core/ao_ignite.c
new file mode 100644 (file)
index 0000000..c7829fc
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_data.h>
+
+__xdata struct ao_ignition ao_ignition[2];
+
+void
+ao_ignite(enum ao_igniter igniter) __critical
+{
+       ao_ignition[igniter].request = 1;
+       ao_wakeup(&ao_ignition);
+}
+
+#ifndef AO_SENSE_DROGUE
+#define AO_SENSE_DROGUE(p)     ((p)->adc.sense_d)
+#define AO_SENSE_MAIN(p)       ((p)->adc.sense_m)
+#endif
+
+enum ao_igniter_status
+ao_igniter_status(enum ao_igniter igniter)
+{
+       __xdata struct ao_data packet;
+       __pdata int16_t value;
+       __pdata uint8_t request, firing, fired;
+
+       __critical {
+               ao_data_get(&packet);
+               request = ao_ignition[igniter].request;
+               fired = ao_ignition[igniter].fired;
+               firing = ao_ignition[igniter].firing;
+       }
+       if (firing || (request && !fired))
+               return ao_igniter_active;
+
+       value = (AO_IGNITER_CLOSED>>1);
+       switch (igniter) {
+       case ao_igniter_drogue:
+               value = AO_SENSE_DROGUE(&packet);
+               break;
+       case ao_igniter_main:
+               value = AO_SENSE_MAIN(&packet);
+               break;
+       }
+       if (value < AO_IGNITER_OPEN)
+               return ao_igniter_open;
+       else if (value > AO_IGNITER_CLOSED)
+               return ao_igniter_ready;
+       else
+               return ao_igniter_unknown;
+}
+
+#ifndef AO_IGNITER_SET_DROGUE
+#define AO_IGNITER_SET_DROGUE(v)       AO_IGNITER_DROGUE = (v)
+#define AO_IGNITER_SET_MAIN(v)         AO_IGNITER_MAIN = (v)
+#endif
+
+#ifndef AO_IGNITER_FIRE_TIME
+#define AO_IGNITER_FIRE_TIME           AO_MS_TO_TICKS(50)
+#endif
+
+#ifndef AO_IGNITER_CHARGE_TIME
+#define AO_IGNITER_CHARGE_TIME         AO_MS_TO_TICKS(2000)
+#endif
+
+void
+ao_igniter_fire(enum ao_igniter igniter) __critical
+{
+       ao_ignition[igniter].firing = 1;
+       switch(ao_config.ignite_mode) {
+       case AO_IGNITE_MODE_DUAL:
+               switch (igniter) {
+               case ao_igniter_drogue:
+                       AO_IGNITER_SET_DROGUE(1);
+                       ao_delay(AO_IGNITER_FIRE_TIME);
+                       AO_IGNITER_SET_DROGUE(0);
+                       break;
+               case ao_igniter_main:
+                       AO_IGNITER_SET_MAIN(1);
+                       ao_delay(AO_IGNITER_FIRE_TIME);
+                       AO_IGNITER_SET_MAIN(0);
+                       break;
+               }
+               break;
+       case AO_IGNITE_MODE_APOGEE:
+               switch (igniter) {
+               case ao_igniter_drogue:
+                       AO_IGNITER_SET_DROGUE(1);
+                       ao_delay(AO_IGNITER_FIRE_TIME);
+                       AO_IGNITER_SET_DROGUE(0);
+                       ao_delay(AO_IGNITER_CHARGE_TIME);
+                       AO_IGNITER_SET_MAIN(1);
+                       ao_delay(AO_IGNITER_FIRE_TIME);
+                       AO_IGNITER_SET_MAIN(0);
+                       break;
+               }
+               break;
+       case AO_IGNITE_MODE_MAIN:
+               switch (igniter) {
+               case ao_igniter_main:
+                       AO_IGNITER_SET_DROGUE(1);
+                       ao_delay(AO_IGNITER_FIRE_TIME);
+                       AO_IGNITER_SET_DROGUE(0);
+                       ao_delay(AO_IGNITER_CHARGE_TIME);
+                       AO_IGNITER_SET_MAIN(1);
+                       ao_delay(AO_IGNITER_FIRE_TIME);
+                       AO_IGNITER_SET_MAIN(0);
+                       break;
+               }
+               break;
+       }
+       ao_ignition[igniter].firing = 0;
+}
+
+void
+ao_igniter(void)
+{
+       __xdata enum ao_igniter igniter;
+
+       ao_config_get();
+       for (;;) {
+               ao_sleep(&ao_ignition);
+               for (igniter = ao_igniter_drogue; igniter <= ao_igniter_main; igniter++) {
+                       if (ao_ignition[igniter].request && !ao_ignition[igniter].fired) {
+                               if (igniter == ao_igniter_drogue && ao_config.apogee_delay)
+                                       ao_delay(AO_SEC_TO_TICKS(ao_config.apogee_delay));
+
+                               ao_igniter_fire(igniter);
+                               ao_delay(AO_IGNITER_CHARGE_TIME);
+                               ao_ignition[igniter].fired = 1;
+                       }
+               }
+       }
+}
+
+void
+ao_ignite_manual(void)
+{
+       ao_cmd_white();
+       if (!ao_match_word("DoIt"))
+               return;
+       ao_cmd_white();
+       if (ao_cmd_lex_c == 'm') {
+               if(ao_match_word("main"))
+                       ao_igniter_fire(ao_igniter_main);
+       } else {
+               if(ao_match_word("drogue"))
+                       ao_igniter_fire(ao_igniter_drogue);
+       }
+}
+
+static __code char * __code igniter_status_names[] = {
+       "unknown", "ready", "active", "open"
+};
+
+void
+ao_ignite_print_status(enum ao_igniter igniter, __code char *name) __reentrant
+{
+       enum ao_igniter_status status = ao_igniter_status(igniter);
+       printf("Igniter: %6s Status: %s\n",
+              name,
+              igniter_status_names[status]);
+}
+
+void
+ao_ignite_test(void)
+{
+       ao_ignite_print_status(ao_igniter_drogue, "drogue");
+       ao_ignite_print_status(ao_igniter_main, "main");
+}
+
+__code struct ao_cmds ao_ignite_cmds[] = {
+       { ao_ignite_manual,     "i <key> {main|drogue}\0Fire igniter. <key> is doit with D&I" },
+       { ao_ignite_test,       "t\0Test igniter" },
+       { 0,    NULL },
+};
+
+__xdata struct ao_task ao_igniter_task;
+
+void
+ao_ignite_set_pins(void)
+{
+       ao_enable_output(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, AO_IGNITER_DROGUE, 0);
+       ao_enable_output(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, AO_IGNITER_MAIN, 0);
+}
+
+void
+ao_igniter_init(void)
+{
+       ao_ignite_set_pins();
+       ao_cmd_register(&ao_ignite_cmds[0]);
+       ao_add_task(&ao_igniter_task, ao_igniter, "igniter");
+}
diff --git a/src/core/ao_kalman.c b/src/core/ao_kalman.c
new file mode 100644 (file)
index 0000000..59ffd8b
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#include "ao_flight.h"
+#endif
+
+#include "ao_sample.h"
+#include "ao_kalman.h"
+
+
+static __pdata int32_t         ao_k_height;
+static __pdata int32_t         ao_k_speed;
+static __pdata int32_t         ao_k_accel;
+
+#define AO_K_STEP_100          to_fix16(0.01)
+#define AO_K_STEP_2_2_100      to_fix16(0.00005)
+
+#define AO_K_STEP_10           to_fix16(0.1)
+#define AO_K_STEP_2_2_10       to_fix16(0.005)
+
+#define AO_K_STEP_1            to_fix16(1)
+#define AO_K_STEP_2_2_1                to_fix16(0.5)
+
+__pdata int16_t                        ao_height;
+__pdata int16_t                        ao_speed;
+__pdata int16_t                        ao_accel;
+__pdata int16_t                        ao_max_height;
+static __pdata int32_t         ao_avg_height_scaled;
+__pdata int16_t                        ao_avg_height;
+
+__pdata int16_t                        ao_error_h;
+__pdata int16_t                        ao_error_h_sq_avg;
+
+#if HAS_ACCEL
+__pdata int16_t                        ao_error_a;
+#endif
+
+static void
+ao_kalman_predict(void)
+{
+#ifdef AO_FLIGHT_TEST
+       if (ao_sample_tick - ao_sample_prev_tick > 50) {
+               ao_k_height += ((int32_t) ao_speed * AO_K_STEP_1 +
+                               (int32_t) ao_accel * AO_K_STEP_2_2_1) >> 4;
+               ao_k_speed += (int32_t) ao_accel * AO_K_STEP_1;
+
+               return;
+       }
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               ao_k_height += ((int32_t) ao_speed * AO_K_STEP_10 +
+                               (int32_t) ao_accel * AO_K_STEP_2_2_10) >> 4;
+               ao_k_speed += (int32_t) ao_accel * AO_K_STEP_10;
+
+               return;
+       }
+       if (ao_flight_debug) {
+               printf ("predict speed %g + (%g * %g) = %g\n",
+                       ao_k_speed / (65536.0 * 16.0), ao_accel / 16.0, AO_K_STEP_100 / 65536.0,
+                       (ao_k_speed + (int32_t) ao_accel * AO_K_STEP_100) / (65536.0 * 16.0));
+       }
+#endif
+       ao_k_height += ((int32_t) ao_speed * AO_K_STEP_100 +
+                       (int32_t) ao_accel * AO_K_STEP_2_2_100) >> 4;
+       ao_k_speed += (int32_t) ao_accel * AO_K_STEP_100;
+}
+
+static void
+ao_kalman_err_height(void)
+{
+       int16_t e;
+       int16_t height_distrust;
+#if HAS_ACCEL
+       int16_t speed_distrust;
+#endif
+
+       ao_error_h = ao_sample_height - (int16_t) (ao_k_height >> 16);
+
+       e = ao_error_h;
+       if (e < 0)
+               e = -e;
+       if (e > 127)
+               e = 127;
+#if HAS_ACCEL
+       ao_error_h_sq_avg -= ao_error_h_sq_avg >> 2;
+       ao_error_h_sq_avg += (e * e) >> 2;
+#else
+       ao_error_h_sq_avg -= ao_error_h_sq_avg >> 4;
+       ao_error_h_sq_avg += (e * e) >> 4;
+#endif
+
+       if (ao_flight_state >= ao_flight_drogue)
+               return;
+       height_distrust = ao_sample_alt - AO_MAX_BARO_HEIGHT;
+#if HAS_ACCEL
+       /* speed is stored * 16, but we need to ramp between 200 and 328, so
+        * we want to multiply by 2. The result is a shift by 3.
+        */
+       speed_distrust = (ao_speed - AO_MS_TO_SPEED(AO_MAX_BARO_SPEED)) >> (4 - 1);
+       if (speed_distrust <= 0)
+               speed_distrust = 0;
+       else if (speed_distrust > height_distrust)
+               height_distrust = speed_distrust;
+#endif
+       if (height_distrust > 0) {
+#ifdef AO_FLIGHT_TEST
+               int     old_ao_error_h = ao_error_h;
+#endif
+               if (height_distrust > 0x100)
+                       height_distrust = 0x100;
+               ao_error_h = (int16_t) (((int32_t) ao_error_h * (0x100 - height_distrust)) >> 8);
+#ifdef AO_FLIGHT_TEST
+               if (ao_flight_debug) {
+                       printf("over height %g over speed %g distrust: %g height: error %d -> %d\n",
+                              (double) (ao_sample_alt - AO_MAX_BARO_HEIGHT),
+                              (ao_speed - AO_MS_TO_SPEED(AO_MAX_BARO_SPEED)) / 16.0,
+                              height_distrust / 256.0,
+                              old_ao_error_h, ao_error_h);
+               }
+#endif
+       }
+}
+
+static void
+ao_kalman_correct_baro(void)
+{
+       ao_kalman_err_height();
+#ifdef AO_FLIGHT_TEST
+       if (ao_sample_tick - ao_sample_prev_tick > 50) {
+               ao_k_height += (int32_t) AO_BARO_K0_1 * ao_error_h;
+               ao_k_speed  += (int32_t) AO_BARO_K1_1 * ao_error_h;
+               ao_k_accel  += (int32_t) AO_BARO_K2_1 * ao_error_h;
+               return;
+       }
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               ao_k_height += (int32_t) AO_BARO_K0_10 * ao_error_h;
+               ao_k_speed  += (int32_t) AO_BARO_K1_10 * ao_error_h;
+               ao_k_accel  += (int32_t) AO_BARO_K2_10 * ao_error_h;
+               return;
+       }
+#endif
+       ao_k_height += (int32_t) AO_BARO_K0_100 * ao_error_h;
+       ao_k_speed  += (int32_t) AO_BARO_K1_100 * ao_error_h;
+       ao_k_accel  += (int32_t) AO_BARO_K2_100 * ao_error_h;
+}
+
+#if HAS_ACCEL
+
+static void
+ao_kalman_err_accel(void)
+{
+       int32_t accel;
+
+       accel = (ao_ground_accel - ao_sample_accel) * ao_accel_scale;
+
+       /* Can't use ao_accel here as it is the pre-prediction value still */
+       ao_error_a = (accel - ao_k_accel) >> 16;
+}
+
+#ifndef FORCE_ACCEL
+static void
+ao_kalman_correct_both(void)
+{
+       ao_kalman_err_height();
+       ao_kalman_err_accel();
+
+#ifdef AO_FLIGHT_TEST
+       if (ao_sample_tick - ao_sample_prev_tick > 50) {
+               if (ao_flight_debug) {
+                       printf ("correct speed %g + (%g * %g) + (%g * %g) = %g\n",
+                               ao_k_speed / (65536.0 * 16.0),
+                               (double) ao_error_h, AO_BOTH_K10_1 / 65536.0,
+                               (double) ao_error_a, AO_BOTH_K11_1 / 65536.0,
+                               (ao_k_speed +
+                                (int32_t) AO_BOTH_K10_1 * ao_error_h +
+                                (int32_t) AO_BOTH_K11_1 * ao_error_a) / (65536.0 * 16.0));
+               }
+               ao_k_height +=
+                       (int32_t) AO_BOTH_K00_1 * ao_error_h +
+                       (int32_t) AO_BOTH_K01_1 * ao_error_a;
+               ao_k_speed +=
+                       (int32_t) AO_BOTH_K10_1 * ao_error_h +
+                       (int32_t) AO_BOTH_K11_1 * ao_error_a;
+               ao_k_accel +=
+                       (int32_t) AO_BOTH_K20_1 * ao_error_h +
+                       (int32_t) AO_BOTH_K21_1 * ao_error_a;
+               return;
+       }
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               if (ao_flight_debug) {
+                       printf ("correct speed %g + (%g * %g) + (%g * %g) = %g\n",
+                               ao_k_speed / (65536.0 * 16.0),
+                               (double) ao_error_h, AO_BOTH_K10_10 / 65536.0,
+                               (double) ao_error_a, AO_BOTH_K11_10 / 65536.0,
+                               (ao_k_speed +
+                                (int32_t) AO_BOTH_K10_10 * ao_error_h +
+                                (int32_t) AO_BOTH_K11_10 * ao_error_a) / (65536.0 * 16.0));
+               }
+               ao_k_height +=
+                       (int32_t) AO_BOTH_K00_10 * ao_error_h +
+                       (int32_t) AO_BOTH_K01_10 * ao_error_a;
+               ao_k_speed +=
+                       (int32_t) AO_BOTH_K10_10 * ao_error_h +
+                       (int32_t) AO_BOTH_K11_10 * ao_error_a;
+               ao_k_accel +=
+                       (int32_t) AO_BOTH_K20_10 * ao_error_h +
+                       (int32_t) AO_BOTH_K21_10 * ao_error_a;
+               return;
+       }
+       if (ao_flight_debug) {
+               printf ("correct speed %g + (%g * %g) + (%g * %g) = %g\n",
+                       ao_k_speed / (65536.0 * 16.0),
+                       (double) ao_error_h, AO_BOTH_K10_100 / 65536.0,
+                       (double) ao_error_a, AO_BOTH_K11_100 / 65536.0,
+                       (ao_k_speed +
+                        (int32_t) AO_BOTH_K10_100 * ao_error_h +
+                        (int32_t) AO_BOTH_K11_100 * ao_error_a) / (65536.0 * 16.0));
+       }
+#endif
+       ao_k_height +=
+               (int32_t) AO_BOTH_K00_100 * ao_error_h +
+               (int32_t) AO_BOTH_K01_100 * ao_error_a;
+       ao_k_speed +=
+               (int32_t) AO_BOTH_K10_100 * ao_error_h +
+               (int32_t) AO_BOTH_K11_100 * ao_error_a;
+       ao_k_accel +=
+               (int32_t) AO_BOTH_K20_100 * ao_error_h +
+               (int32_t) AO_BOTH_K21_100 * ao_error_a;
+}
+
+#else
+
+static void
+ao_kalman_correct_accel(void)
+{
+       ao_kalman_err_accel();
+
+       if (ao_sample_tick - ao_sample_prev_tick > 5) {
+               ao_k_height +=(int32_t) AO_ACCEL_K0_10 * ao_error_a;
+               ao_k_speed  += (int32_t) AO_ACCEL_K1_10 * ao_error_a;
+               ao_k_accel  += (int32_t) AO_ACCEL_K2_10 * ao_error_a;
+               return;
+       }
+       ao_k_height += (int32_t) AO_ACCEL_K0_100 * ao_error_a;
+       ao_k_speed  += (int32_t) AO_ACCEL_K1_100 * ao_error_a;
+       ao_k_accel  += (int32_t) AO_ACCEL_K2_100 * ao_error_a;
+}
+
+#endif /* else FORCE_ACCEL */
+#endif /* HAS_ACCEL */
+
+void
+ao_kalman(void)
+{
+       ao_kalman_predict();
+#if HAS_ACCEL
+       if (ao_flight_state <= ao_flight_coast) {
+#ifdef FORCE_ACCEL
+               ao_kalman_correct_accel();
+#else
+               ao_kalman_correct_both();
+#endif
+       } else
+#endif
+               ao_kalman_correct_baro();
+       ao_height = from_fix(ao_k_height);
+       ao_speed = from_fix(ao_k_speed);
+       ao_accel = from_fix(ao_k_accel);
+       if (ao_height > ao_max_height)
+               ao_max_height = ao_height;
+       ao_avg_height_scaled = ao_avg_height_scaled - ao_avg_height + ao_sample_height;
+#ifdef AO_FLIGHT_TEST
+       if (ao_sample_tick - ao_sample_prev_tick > 50)
+               ao_avg_height = (ao_avg_height_scaled + 1) >> 1;
+       else if (ao_sample_tick - ao_sample_prev_tick > 5)
+               ao_avg_height = (ao_avg_height_scaled + 7) >> 4;
+       else 
+#endif
+               ao_avg_height = (ao_avg_height_scaled + 63) >> 7;
+#ifdef AO_FLIGHT_TEST
+       ao_sample_prev_tick = ao_sample_tick;
+#endif
+}
diff --git a/src/core/ao_lcd.h b/src/core/ao_lcd.h
new file mode 100644 (file)
index 0000000..f7e1391
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LCD_H_
+#define _AO_LCD_H_
+
+/* ao_lcd.c */
+  
+void
+ao_lcd_putchar(uint8_t d);
+
+void
+ao_lcd_putstring(char *string);
+
+void
+ao_lcd_contrast_set(uint8_t contrast);
+
+void
+ao_lcd_clear(void);
+
+void
+ao_lcd_cursor_on(void);
+
+void
+ao_lcd_cursor_off(void);
+
+#define AO_LCD_ADDR(row,col)   ((row << 6) | (col))
+
+void
+ao_lcd_goto(uint8_t addr);
+
+void
+ao_lcd_start(void);
+
+void
+ao_lcd_init(void);
+
+/* ao_lcd_port.c */
+
+void
+ao_lcd_port_put_nibble(uint8_t rs, uint8_t d);
+
+void
+ao_lcd_port_init(void);
+
+#endif /* _AO_LCD_H_ */
diff --git a/src/core/ao_led.h b/src/core/ao_led.h
new file mode 100644 (file)
index 0000000..d9a0914
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LED_H_
+#define _AO_LED_H_
+
+/*
+ * ao_led.c
+ */
+
+#define AO_LED_NONE    0
+
+#ifndef AO_LED_TYPE
+#define AO_LED_TYPE uint8_t
+#endif
+
+/* Turn on the specified LEDs */
+void
+ao_led_on(AO_LED_TYPE colors);
+
+/* Turn off the specified LEDs */
+void
+ao_led_off(AO_LED_TYPE colors);
+
+/* Set all of the LEDs to the specified state */
+void
+ao_led_set(AO_LED_TYPE colors);
+
+/* Set all LEDs in 'mask' to the specified state */
+void
+ao_led_set_mask(uint8_t colors, uint8_t mask);
+
+/* Toggle the specified LEDs */
+void
+ao_led_toggle(AO_LED_TYPE colors);
+
+/* Turn on the specified LEDs for the indicated interval */
+void
+ao_led_for(AO_LED_TYPE colors, uint16_t ticks) __reentrant;
+
+/* Initialize the LEDs */
+void
+ao_led_init(AO_LED_TYPE enable);
+
+#endif /* _AO_LED_H_ */
diff --git a/src/core/ao_log.c b/src/core/ao_log.c
new file mode 100644 (file)
index 0000000..7884ec3
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_log.h>
+
+__pdata uint32_t ao_log_current_pos;
+__pdata uint32_t ao_log_end_pos;
+__pdata uint32_t ao_log_start_pos;
+__xdata uint8_t        ao_log_running;
+__pdata enum ao_flight_state ao_log_state;
+__xdata uint16_t ao_flight_number;
+
+void
+ao_log_flush(void)
+{
+       ao_storage_flush();
+}
+
+/*
+ * When erasing a flight log, make sure the config block
+ * has an up-to-date version of the current flight number
+ */
+
+struct ao_log_erase {
+       uint8_t unused;
+       uint16_t flight;
+};
+
+static __xdata struct ao_log_erase erase;
+
+#define LOG_MAX_ERASE  16
+
+static uint32_t
+ao_log_erase_pos(uint8_t i)
+{
+       return i * sizeof (struct ao_log_erase) + AO_STORAGE_ERASE_LOG;
+}
+
+void
+ao_log_write_erase(uint8_t pos)
+{
+       erase.unused = 0x00;
+       erase.flight = ao_flight_number;
+       ao_storage_write(ao_log_erase_pos(pos),  &erase, sizeof (erase));
+       ao_storage_flush();
+}
+
+static void
+ao_log_read_erase(uint8_t pos)
+{
+       ao_storage_read(ao_log_erase_pos(pos), &erase, sizeof (erase));
+}
+
+
+static void
+ao_log_erase_mark(void)
+{
+       uint8_t                         i;
+
+       for (i = 0; i < LOG_MAX_ERASE; i++) {
+               ao_log_read_erase(i);
+               if (erase.unused == 0 && erase.flight == ao_flight_number)
+                       return;
+               if (erase.unused == 0xff) {
+                       ao_log_write_erase(i);
+                       return;
+               }
+       }
+       ao_config_put();
+}
+
+static uint8_t
+ao_log_slots()
+{
+       return (uint8_t) (ao_storage_config / ao_config.flight_log_max);
+}
+
+uint32_t
+ao_log_pos(uint8_t slot)
+{
+       return ((slot) * ao_config.flight_log_max);
+}
+
+static uint16_t
+ao_log_max_flight(void)
+{
+       uint8_t         log_slot;
+       uint8_t         log_slots;
+       uint16_t        log_flight;
+       uint16_t        max_flight = 0;
+
+       /* Scan the log space looking for the biggest flight number */
+       log_slots = ao_log_slots();
+       for (log_slot = 0; log_slot < log_slots; log_slot++) {
+               log_flight = ao_log_flight(log_slot);
+               if (!log_flight)
+                       continue;
+               if (max_flight == 0 || (int16_t) (log_flight - max_flight) > 0)
+                       max_flight = log_flight;
+       }
+       return max_flight;
+}
+
+void
+ao_log_scan(void) __reentrant
+{
+       uint8_t         log_slot;
+       uint8_t         log_slots;
+       uint8_t         log_want;
+
+       ao_config_get();
+
+       ao_flight_number = ao_log_max_flight();
+       if (ao_flight_number)
+               if (++ao_flight_number == 0)
+                       ao_flight_number = 1;
+
+       /* Now look through the log of flight numbers from erase operations and
+        * see if the last one is bigger than what we found above
+        */
+       for (log_slot = LOG_MAX_ERASE; log_slot-- > 0;) {
+               ao_log_read_erase(log_slot);
+               if (erase.unused == 0) {
+                       if (ao_flight_number == 0 ||
+                           (int16_t) (erase.flight - ao_flight_number) > 0)
+                               ao_flight_number = erase.flight;
+                       break;
+               }
+       }
+       if (ao_flight_number == 0)
+               ao_flight_number = 1;
+
+       /* With a flight number in hand, find a place to write a new log,
+        * use the target flight number to index the available log slots so
+        * that we write logs to each spot about the same number of times.
+        */
+
+       /* Find a log slot for the next flight, if available */
+       ao_log_current_pos = ao_log_end_pos = 0;
+       log_slots = ao_log_slots();
+       log_want = (ao_flight_number - 1) % log_slots;
+       log_slot = log_want;
+       do {
+               if (ao_log_flight(log_slot) == 0) {
+                       ao_log_current_pos = ao_log_pos(log_slot);
+                       ao_log_end_pos = ao_log_current_pos + ao_config.flight_log_max;
+                       break;
+               }
+               if (++log_slot >= log_slots)
+                       log_slot = 0;
+       } while (log_slot != log_want);
+
+       ao_wakeup(&ao_flight_number);
+}
+
+void
+ao_log_start(void)
+{
+       /* start logging */
+       ao_log_running = 1;
+       ao_wakeup(&ao_log_running);
+}
+
+void
+ao_log_stop(void)
+{
+       ao_log_running = 0;
+       ao_log_flush();
+}
+
+uint8_t
+ao_log_present(void)
+{
+       return ao_log_max_flight() != 0;
+}
+
+uint8_t
+ao_log_full(void)
+{
+       return ao_log_current_pos == ao_log_end_pos;
+}
+
+static __xdata struct ao_task ao_log_task;
+
+void
+ao_log_list(void) __reentrant
+{
+       uint8_t slot;
+       uint8_t slots;
+       uint16_t flight;
+
+       slots = ao_log_slots();
+       for (slot = 0; slot < slots; slot++)
+       {
+               flight = ao_log_flight(slot);
+               if (flight)
+                       printf ("flight %d start %x end %x\n",
+                               flight,
+                               (uint16_t) (ao_log_pos(slot) >> 8),
+                               (uint16_t) (ao_log_pos(slot+1) >> 8));
+       }
+       printf ("done\n");
+}
+
+void
+ao_log_delete(void) __reentrant
+{
+       uint8_t slot;
+       uint8_t slots;
+
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+
+       slots = ao_log_slots();
+       /* Look for the flight log matching the requested flight */
+       if (ao_cmd_lex_i) {
+               for (slot = 0; slot < slots; slot++) {
+                       if (ao_log_flight(slot) == ao_cmd_lex_i) {
+                               ao_log_erase_mark();
+                               ao_log_current_pos = ao_log_pos(slot);
+                               ao_log_end_pos = ao_log_current_pos + ao_config.flight_log_max;
+                               while (ao_log_current_pos < ao_log_end_pos) {
+                                       uint8_t i;
+                                       static __xdata uint8_t b;
+
+                                       /*
+                                        * Check to see if we've reached the end of
+                                        * the used memory to avoid re-erasing the same
+                                        * memory over and over again
+                                        */
+                                       for (i = 0; i < 16; i++) {
+                                               if (ao_storage_read(ao_log_current_pos + i, &b, 1))
+                                                       if (b != 0xff)
+                                                               break;
+                                       }
+                                       if (i == 16)
+                                               break;
+                                       ao_storage_erase(ao_log_current_pos);
+                                       ao_log_current_pos += ao_storage_block;
+                               }
+                               puts("Erased");
+                               return;
+                       }
+               }
+       }
+       printf("No such flight: %d\n", ao_cmd_lex_i);
+}
+
+__code struct ao_cmds ao_log_cmds[] = {
+       { ao_log_list,  "l\0List logs" },
+       { ao_log_delete,        "d <flight-number>\0Delete flight" },
+       { 0,    NULL },
+};
+
+void
+ao_log_init(void)
+{
+       ao_log_running = 0;
+
+       /* For now, just log the flight starting at the begining of eeprom */
+       ao_log_state = ao_flight_invalid;
+
+       ao_cmd_register(&ao_log_cmds[0]);
+
+       /* Create a task to log events to eeprom */
+       ao_add_task(&ao_log_task, ao_log, "log");
+}
diff --git a/src/core/ao_log.h b/src/core/ao_log.h
new file mode 100644 (file)
index 0000000..04abeb7
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LOG_H_
+#define _AO_LOG_H_
+
+#include <ao_flight.h>
+
+/*
+ * ao_log.c
+ */
+
+/* We record flight numbers in the first record of
+ * the log. Tasks may wait for this to be initialized
+ * by sleeping on this variable.
+ */
+extern __xdata uint16_t ao_flight_number;
+
+extern __pdata uint32_t ao_log_current_pos;
+extern __pdata uint32_t ao_log_end_pos;
+extern __pdata uint32_t ao_log_start_pos;
+extern __xdata uint8_t ao_log_running;
+extern __pdata enum ao_flight_state ao_log_state;
+
+/* required functions from the underlying log system */
+
+#define AO_LOG_FORMAT_UNKNOWN          0       /* unknown; altosui will have to guess */
+#define AO_LOG_FORMAT_FULL             1       /* 8 byte typed log records */
+#define AO_LOG_FORMAT_TINY             2       /* two byte state/baro records */
+#define AO_LOG_FORMAT_TELEMETRY                3       /* 32 byte ao_telemetry records */
+#define AO_LOG_FORMAT_TELESCIENCE      4       /* 32 byte typed telescience records */
+#define AO_LOG_FORMAT_MEGAMETRUM       5       /* 32 byte typed megametrum records */
+#define AO_LOG_FORMAT_NONE             127     /* No log at all */
+
+extern __code uint8_t ao_log_format;
+
+/* Return the flight number from the given log slot, 0 if none */
+uint16_t
+ao_log_flight(uint8_t slot);
+
+/* Flush the log */
+void
+ao_log_flush(void);
+
+/* Logging thread main routine */
+void
+ao_log(void);
+
+/* functions provided in ao_log.c */
+
+/* Figure out the current flight number */
+void
+ao_log_scan(void) __reentrant;
+
+/* Return the position of the start of the given log slot */
+uint32_t
+ao_log_pos(uint8_t slot);
+
+/* Start logging to eeprom */
+void
+ao_log_start(void);
+
+/* Stop logging */
+void
+ao_log_stop(void);
+
+/* Initialize the logging system */
+void
+ao_log_init(void);
+
+/* Write out the current flight number to the erase log */
+void
+ao_log_write_erase(uint8_t pos);
+
+/* Returns true if there are any logs stored in eeprom */
+uint8_t
+ao_log_present(void);
+
+/* Returns true if there is no more storage space available */
+uint8_t
+ao_log_full(void);
+
+/*
+ * ao_log_big.c
+ */
+
+/*
+ * The data log is recorded in the eeprom as a sequence
+ * of data packets.
+ *
+ * Each packet starts with a 4-byte header that has the
+ * packet type, the packet checksum and the tick count. Then
+ * they all contain 2 16 bit values which hold packet-specific
+ * data.
+ *
+ * For each flight, the first packet
+ * is FLIGHT packet, indicating the serial number of the
+ * device and a unique number marking the number of flights
+ * recorded by this device.
+ *
+ * During flight, data from the accelerometer and barometer
+ * are recorded in SENSOR packets, using the raw 16-bit values
+ * read from the A/D converter.
+ *
+ * Also during flight, but at a lower rate, the deployment
+ * sensors are recorded in DEPLOY packets. The goal here is to
+ * detect failure in the deployment circuits.
+ *
+ * STATE packets hold state transitions as the flight computer
+ * transitions through different stages of the flight.
+ */
+#define AO_LOG_FLIGHT          'F'
+#define AO_LOG_SENSOR          'A'
+#define AO_LOG_TEMP_VOLT       'T'
+#define AO_LOG_DEPLOY          'D'
+#define AO_LOG_STATE           'S'
+#define AO_LOG_GPS_TIME                'G'
+#define AO_LOG_GPS_LAT         'N'
+#define AO_LOG_GPS_LON         'W'
+#define AO_LOG_GPS_ALT         'H'
+#define AO_LOG_GPS_SAT         'V'
+#define AO_LOG_GPS_DATE                'Y'
+
+#define AO_LOG_POS_NONE                (~0UL)
+
+struct ao_log_record {
+       char                    type;
+       uint8_t                 csum;
+       uint16_t                tick;
+       union {
+               struct {
+                       int16_t         ground_accel;
+                       uint16_t        flight;
+               } flight;
+               struct {
+                       int16_t         accel;
+                       int16_t         pres;
+               } sensor;
+               struct {
+                       int16_t         temp;
+                       int16_t         v_batt;
+               } temp_volt;
+               struct {
+                       int16_t         drogue;
+                       int16_t         main;
+               } deploy;
+               struct {
+                       uint16_t        state;
+                       uint16_t        reason;
+               } state;
+               struct {
+                       uint8_t         hour;
+                       uint8_t         minute;
+                       uint8_t         second;
+                       uint8_t         flags;
+               } gps_time;
+               int32_t         gps_latitude;
+               int32_t         gps_longitude;
+               struct {
+                       int16_t         altitude;
+                       uint16_t        unused;
+               } gps_altitude;
+               struct {
+                       uint16_t        svid;
+                       uint8_t         unused;
+                       uint8_t         c_n;
+               } gps_sat;
+               struct {
+                       uint8_t         year;
+                       uint8_t         month;
+                       uint8_t         day;
+                       uint8_t         extra;
+               } gps_date;
+               struct {
+                       uint16_t        d0;
+                       uint16_t        d1;
+               } anon;
+       } u;
+};
+
+struct ao_log_mega {
+       char                    type;                   /* 0 */
+       uint8_t                 csum;                   /* 1 */
+       uint16_t                tick;                   /* 2 */
+       union {                                         /* 4 */
+               struct {
+                       uint16_t        flight;         /* 4 */
+                       int16_t         ground_accel;   /* 6 */
+                       uint32_t        ground_pres;    /* 8 */
+                       uint32_t        ground_temp;    /* 12 */
+               } flight;                               /* 16 */
+               struct {
+                       uint16_t        state;
+                       uint16_t        reason;
+               } state;
+               struct {
+                       uint32_t        pres;           /* 4 */
+                       uint32_t        temp;           /* 8 */
+                       int16_t         accel_x;        /* 12 */
+                       int16_t         accel_y;        /* 14 */
+                       int16_t         accel_z;        /* 16 */
+                       int16_t         gyro_x;         /* 18 */
+                       int16_t         gyro_y;         /* 20 */
+                       int16_t         gyro_z;         /* 22 */
+                       int16_t         mag_x;          /* 24 */
+                       int16_t         mag_y;          /* 26 */
+                       int16_t         mag_z;          /* 28 */
+                       int16_t         accel;          /* 30 */
+               } sensor;       /* 32 */
+               struct {
+                       int16_t         v_batt;         /* 4 */
+                       int16_t         v_pbatt;        /* 6 */
+                       int16_t         n_sense;        /* 8 */
+                       int16_t         sense[10];      /* 10 */
+               } volt;                                 /* 30 */
+               struct {
+                       int32_t         latitude;       /* 4 */
+                       int32_t         longitude;      /* 8 */
+                       int16_t         altitude;       /* 12 */
+                       uint8_t         hour;           /* 14 */
+                       uint8_t         minute;         /* 15 */
+                       uint8_t         second;         /* 16 */
+                       uint8_t         flags;          /* 17 */
+                       uint8_t         year;           /* 18 */
+                       uint8_t         month;          /* 19 */
+                       uint8_t         day;            /* 20 */
+                       uint8_t         pad;            /* 21 */
+               } gps;  /* 22 */
+               struct {
+                       uint16_t        channels;       /* 4 */
+                       struct {
+                               uint8_t svid;
+                               uint8_t c_n;
+                       } sats[12];                     /* 6 */
+               } gps_sat;                              /* 30 */
+       } u;
+};
+
+/* Write a record to the eeprom log */
+uint8_t
+ao_log_data(__xdata struct ao_log_record *log) __reentrant;
+
+uint8_t
+ao_log_mega(__xdata struct ao_log_mega *log) __reentrant;
+
+#endif /* _AO_LOG_H_ */
diff --git a/src/core/ao_log_big.c b/src/core/ao_log_big.c
new file mode 100644 (file)
index 0000000..db01f46
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static __xdata uint8_t ao_log_mutex;
+static __xdata struct ao_log_record log;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_FULL;
+
+static uint8_t
+ao_log_csum(__xdata uint8_t *b) __reentrant
+{
+       uint8_t sum = 0x5a;
+       uint8_t i;
+
+       for (i = 0; i < sizeof (struct ao_log_record); i++)
+               sum += *b++;
+       return -sum;
+}
+
+uint8_t
+ao_log_data(__xdata struct ao_log_record *log) __reentrant
+{
+       uint8_t wrote = 0;
+       /* set checksum */
+       log->csum = 0;
+       log->csum = ao_log_csum((__xdata uint8_t *) log);
+       ao_mutex_get(&ao_log_mutex); {
+               if (ao_log_current_pos >= ao_log_end_pos && ao_log_running)
+                       ao_log_stop();
+               if (ao_log_running) {
+                       wrote = 1;
+                       ao_storage_write(ao_log_current_pos,
+                                        log,
+                                        sizeof (struct ao_log_record));
+                       ao_log_current_pos += sizeof (struct ao_log_record);
+               }
+       } ao_mutex_put(&ao_log_mutex);
+       return wrote;
+}
+
+static uint8_t
+ao_log_dump_check_data(void)
+{
+       if (ao_log_csum((uint8_t *) &log) != 0)
+               return 0;
+       return 1;
+}
+
+static __data uint8_t  ao_log_data_pos;
+
+/* a hack to make sure that ao_log_records fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_record))] ;
+
+#ifndef AO_SENSOR_INTERVAL_ASCENT
+#define AO_SENSOR_INTERVAL_ASCENT      1
+#define AO_SENSOR_INTERVAL_DESCENT     10
+#define AO_OTHER_INTERVAL              32
+#endif
+
+void
+ao_log(void)
+{
+       __pdata uint16_t        next_sensor, next_other;
+
+       ao_storage_setup();
+
+       ao_log_scan();
+
+       while (!ao_log_running)
+               ao_sleep(&ao_log_running);
+
+       log.type = AO_LOG_FLIGHT;
+       log.tick = ao_sample_tick;
+#if HAS_ACCEL
+       log.u.flight.ground_accel = ao_ground_accel;
+#endif
+       log.u.flight.flight = ao_flight_number;
+       ao_log_data(&log);
+
+       /* Write the whole contents of the ring to the log
+        * when starting up.
+        */
+       ao_log_data_pos = ao_data_ring_next(ao_sample_data);
+       next_other = next_sensor = ao_data_ring[ao_log_data_pos].tick;
+       ao_log_state = ao_flight_startup;
+       for (;;) {
+               /* Write samples to EEPROM */
+               while (ao_log_data_pos != ao_sample_data) {
+                       log.tick = ao_data_ring[ao_log_data_pos].tick;
+                       if ((int16_t) (log.tick - next_sensor) >= 0) {
+                               log.type = AO_LOG_SENSOR;
+                               log.u.sensor.accel = ao_data_ring[ao_log_data_pos].adc.accel;
+                               log.u.sensor.pres = ao_data_ring[ao_log_data_pos].adc.pres;
+                               ao_log_data(&log);
+                               if (ao_log_state <= ao_flight_coast)
+                                       next_sensor = log.tick + AO_SENSOR_INTERVAL_ASCENT;
+                               else
+                                       next_sensor = log.tick + AO_SENSOR_INTERVAL_DESCENT;
+                       }
+                       if ((int16_t) (log.tick - next_other) >= 0) {
+                               log.type = AO_LOG_TEMP_VOLT;
+                               log.u.temp_volt.temp = ao_data_ring[ao_log_data_pos].adc.temp;
+                               log.u.temp_volt.v_batt = ao_data_ring[ao_log_data_pos].adc.v_batt;
+                               ao_log_data(&log);
+                               log.type = AO_LOG_DEPLOY;
+                               log.u.deploy.drogue = ao_data_ring[ao_log_data_pos].adc.sense_d;
+                               log.u.deploy.main = ao_data_ring[ao_log_data_pos].adc.sense_m;
+                               ao_log_data(&log);
+                               next_other = log.tick + AO_OTHER_INTERVAL;
+                       }
+                       ao_log_data_pos = ao_data_ring_next(ao_log_data_pos);
+               }
+               /* Write state change to EEPROM */
+               if (ao_flight_state != ao_log_state) {
+                       ao_log_state = ao_flight_state;
+                       log.type = AO_LOG_STATE;
+                       log.tick = ao_sample_tick;
+                       log.u.state.state = ao_log_state;
+                       log.u.state.reason = 0;
+                       ao_log_data(&log);
+
+                       if (ao_log_state == ao_flight_landed)
+                               ao_log_stop();
+               }
+
+               /* Wait for a while */
+               ao_delay(AO_MS_TO_TICKS(100));
+
+               /* Stop logging when told to */
+               while (!ao_log_running)
+                       ao_sleep(&ao_log_running);
+       }
+}
+
+uint16_t
+ao_log_flight(uint8_t slot)
+{
+       if (!ao_storage_read(ao_log_pos(slot),
+                            &log,
+                            sizeof (struct ao_log_record)))
+               return 0;
+
+       if (ao_log_dump_check_data() && log.type == AO_LOG_FLIGHT)
+               return log.u.flight.flight;
+       return 0;
+}
diff --git a/src/core/ao_log_mega.c b/src/core/ao_log_mega.c
new file mode 100644 (file)
index 0000000..ac1590d
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_log.h>
+#include <ao_data.h>
+#include <ao_flight.h>
+
+static __xdata uint8_t ao_log_mutex;
+static __xdata struct ao_log_mega log;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_MEGAMETRUM;
+
+static uint8_t
+ao_log_csum(__xdata uint8_t *b) __reentrant
+{
+       uint8_t sum = 0x5a;
+       uint8_t i;
+
+       for (i = 0; i < sizeof (struct ao_log_mega); i++)
+               sum += *b++;
+       return -sum;
+}
+
+uint8_t
+ao_log_mega(__xdata struct ao_log_mega *log) __reentrant
+{
+       uint8_t wrote = 0;
+       /* set checksum */
+       log->csum = 0;
+       log->csum = ao_log_csum((__xdata uint8_t *) log);
+       ao_mutex_get(&ao_log_mutex); {
+               if (ao_log_current_pos >= ao_log_end_pos && ao_log_running)
+                       ao_log_stop();
+               if (ao_log_running) {
+                       wrote = 1;
+                       ao_storage_write(ao_log_current_pos,
+                                        log,
+                                        sizeof (struct ao_log_mega));
+                       ao_log_current_pos += sizeof (struct ao_log_mega);
+               }
+       } ao_mutex_put(&ao_log_mutex);
+       return wrote;
+}
+
+static uint8_t
+ao_log_dump_check_data(void)
+{
+       if (ao_log_csum((uint8_t *) &log) != 0)
+               return 0;
+       return 1;
+}
+
+static __data uint8_t  ao_log_data_pos;
+
+/* a hack to make sure that ao_log_megas fill the eeprom block in even units */
+typedef uint8_t check_log_size[1-(256 % sizeof(struct ao_log_mega))] ;
+
+#ifndef AO_SENSOR_INTERVAL_ASCENT
+#define AO_SENSOR_INTERVAL_ASCENT      1
+#define AO_SENSOR_INTERVAL_DESCENT     10
+#define AO_OTHER_INTERVAL              32
+#endif
+
+void
+ao_log(void)
+{
+       __pdata uint16_t        next_sensor, next_other;
+       uint8_t                 i;
+
+       ao_storage_setup();
+
+       ao_log_scan();
+
+       while (!ao_log_running)
+               ao_sleep(&ao_log_running);
+
+#if HAS_FLIGHT
+       log.type = AO_LOG_FLIGHT;
+       log.tick = ao_sample_tick;
+#if HAS_ACCEL
+       log.u.flight.ground_accel = ao_ground_accel;
+#endif
+       log.u.flight.ground_pres = ao_ground_pres;
+       log.u.flight.flight = ao_flight_number;
+       ao_log_mega(&log);
+#endif
+
+       /* Write the whole contents of the ring to the log
+        * when starting up.
+        */
+       ao_log_data_pos = ao_data_ring_next(ao_data_head);
+       next_other = next_sensor = ao_data_ring[ao_log_data_pos].tick;
+       ao_log_state = ao_flight_startup;
+       for (;;) {
+               /* Write samples to EEPROM */
+               while (ao_log_data_pos != ao_data_head) {
+                       log.tick = ao_data_ring[ao_log_data_pos].tick;
+                       if ((int16_t) (log.tick - next_sensor) >= 0) {
+                               log.type = AO_LOG_SENSOR;
+#if HAS_MS5607
+                               log.u.sensor.pres = ao_data_ring[ao_log_data_pos].ms5607_raw.pres;
+                               log.u.sensor.temp = ao_data_ring[ao_log_data_pos].ms5607_raw.temp;
+#endif
+#if HAS_MPU6000
+                               log.u.sensor.accel_x = ao_data_ring[ao_log_data_pos].mpu6000.accel_x;
+                               log.u.sensor.accel_y = ao_data_ring[ao_log_data_pos].mpu6000.accel_y;
+                               log.u.sensor.accel_z = ao_data_ring[ao_log_data_pos].mpu6000.accel_z;
+                               log.u.sensor.gyro_x = ao_data_ring[ao_log_data_pos].mpu6000.gyro_x;
+                               log.u.sensor.gyro_y = ao_data_ring[ao_log_data_pos].mpu6000.gyro_y;
+                               log.u.sensor.gyro_z = ao_data_ring[ao_log_data_pos].mpu6000.gyro_z;
+#endif
+#if HAS_HMC5883
+                               log.u.sensor.mag_x = ao_data_ring[ao_log_data_pos].hmc5883.x;
+                               log.u.sensor.mag_y = ao_data_ring[ao_log_data_pos].hmc5883.y;
+                               log.u.sensor.mag_z = ao_data_ring[ao_log_data_pos].hmc5883.z;
+#endif
+                               log.u.sensor.accel = ao_data_accel(&ao_data_ring[ao_log_data_pos]);
+                               ao_log_mega(&log);
+                               if (ao_log_state <= ao_flight_coast)
+                                       next_sensor = log.tick + AO_SENSOR_INTERVAL_ASCENT;
+                               else
+                                       next_sensor = log.tick + AO_SENSOR_INTERVAL_DESCENT;
+                       }
+                       if ((int16_t) (log.tick - next_other) >= 0) {
+                               log.type = AO_LOG_TEMP_VOLT;
+                               log.u.volt.v_batt = ao_data_ring[ao_log_data_pos].adc.v_batt;
+                               log.u.volt.v_pbatt = ao_data_ring[ao_log_data_pos].adc.v_pbatt;
+                               log.u.volt.n_sense = AO_ADC_NUM_SENSE;
+                               for (i = 0; i < AO_ADC_NUM_SENSE; i++)
+                                       log.u.volt.sense[i] = ao_data_ring[ao_log_data_pos].adc.sense[i];
+                               ao_log_mega(&log);
+                               next_other = log.tick + AO_OTHER_INTERVAL;
+                       }
+                       ao_log_data_pos = ao_data_ring_next(ao_log_data_pos);
+               }
+#if HAS_FLIGHT
+               /* Write state change to EEPROM */
+               if (ao_flight_state != ao_log_state) {
+                       ao_log_state = ao_flight_state;
+                       log.type = AO_LOG_STATE;
+                       log.tick = ao_time();
+                       log.u.state.state = ao_log_state;
+                       log.u.state.reason = 0;
+                       ao_log_mega(&log);
+
+                       if (ao_log_state == ao_flight_landed)
+                               ao_log_stop();
+               }
+#endif
+
+               /* Wait for a while */
+               ao_delay(AO_MS_TO_TICKS(100));
+
+               /* Stop logging when told to */
+               while (!ao_log_running)
+                       ao_sleep(&ao_log_running);
+       }
+}
+
+uint16_t
+ao_log_flight(uint8_t slot)
+{
+       if (!ao_storage_read(ao_log_pos(slot),
+                            &log,
+                            sizeof (struct ao_log_mega)))
+               return 0;
+
+       if (ao_log_dump_check_data() && log.type == AO_LOG_FLIGHT)
+               return log.u.flight.flight;
+       return 0;
+}
diff --git a/src/core/ao_log_single.c b/src/core/ao_log_single.c
new file mode 100644 (file)
index 0000000..3f6235a
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+/*
+ * ao_log_single.c
+ *
+ * Stores a sequence of fixed-size (32 byte) chunks
+ * without splitting memory up into separate flights
+ */
+
+#include "ao.h"
+#include "ao_product.h"
+
+static __xdata struct ao_task ao_log_single_task;
+
+__xdata uint8_t        ao_log_running;
+__xdata uint8_t                ao_log_mutex;
+__pdata uint32_t       ao_log_start_pos;
+__pdata uint32_t       ao_log_end_pos;
+__pdata uint32_t       ao_log_current_pos;
+
+__xdata union ao_log_single ao_log_single_write_data;
+__xdata union ao_log_single ao_log_single_read_data;
+
+uint8_t
+ao_log_single_write(void)
+{
+       uint8_t wrote = 0;
+
+       ao_mutex_get(&ao_log_mutex); {
+               if (ao_log_current_pos >= ao_log_end_pos && ao_log_running)
+                       ao_log_single_stop();
+               if (ao_log_running) {
+                       wrote = 1;
+                       ao_storage_write(ao_log_current_pos,
+                                        &ao_log_single_write_data,
+                                        AO_LOG_SINGLE_SIZE);
+                       ao_log_current_pos += AO_LOG_SINGLE_SIZE;
+               }
+       } ao_mutex_put(&ao_log_mutex);
+       return wrote;
+}
+
+static uint8_t
+ao_log_single_valid(void)
+{
+       __xdata uint8_t *d = ao_log_single_read_data.bytes;
+       uint8_t i;
+       for (i = 0; i < AO_LOG_SINGLE_SIZE; i++)
+               if (*d++ != 0xff)
+                       return 1;
+       return 0;
+}
+
+uint8_t
+ao_log_single_read(uint32_t pos)
+{
+       if (!ao_storage_read(pos, &ao_log_single_read_data, AO_LOG_SINGLE_SIZE))
+               return 0;
+       return ao_log_single_valid();
+}
+
+void
+ao_log_single_start(void)
+{
+       if (!ao_log_running) {
+               ao_log_running = 1;
+               ao_wakeup(&ao_log_running);
+       }
+}
+
+void
+ao_log_single_stop(void)
+{
+       if (ao_log_running) {
+               ao_log_running = 0;
+       }
+}
+
+void
+ao_log_single_restart(void)
+{
+       /* Find end of data */
+       ao_log_end_pos = ao_storage_config;
+       for (ao_log_current_pos = 0;
+            ao_log_current_pos < ao_storage_config;
+            ao_log_current_pos += ao_storage_block)
+       {
+               if (!ao_log_single_read(ao_log_current_pos))
+                       break;
+       }
+       if (ao_log_current_pos > 0) {
+               ao_log_current_pos -= ao_storage_block;
+               for (; ao_log_current_pos < ao_storage_config;
+                    ao_log_current_pos += sizeof (struct ao_log_telescience))
+               {
+                       if (!ao_log_single_read(ao_log_current_pos))
+                               break;
+               }
+       }
+}
+
+void
+ao_log_single_set(void)
+{
+       printf("Logging currently %s\n", ao_log_running ? "on" : "off");
+       ao_cmd_hex();
+       if (ao_cmd_status == ao_cmd_success) {
+               if (ao_cmd_lex_i) {
+                       printf("Logging from %ld to %ld\n", ao_log_current_pos, ao_log_end_pos);
+                       ao_log_single_start();
+               } else {
+                       printf ("Log stopped at %ld\n", ao_log_current_pos);
+                       ao_log_single_stop();
+               }
+       }
+       ao_cmd_status = ao_cmd_success;
+}
+
+void
+ao_log_single_delete(void)
+{
+       uint32_t        pos;
+
+       ao_cmd_hex();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       if (ao_cmd_lex_i != 1) {
+               ao_cmd_status = ao_cmd_syntax_error;
+               printf("No such flight: %d\n", ao_cmd_lex_i);
+               return;
+       }
+       ao_log_single_stop();
+       for (pos = 0; pos < ao_storage_config; pos += ao_storage_block) {
+               if (!ao_log_single_read(pos))
+                       break;
+               ao_storage_erase(pos);
+       }
+       ao_log_current_pos = ao_log_start_pos = 0;
+       if (pos == 0)
+               printf("No such flight: %d\n", ao_cmd_lex_i);
+       else
+               printf ("Erased\n");
+}
+
+uint8_t
+ao_log_full(void)
+{
+       return ao_log_current_pos >= ao_log_end_pos;
+}
+
+uint8_t
+ao_log_present(void)
+{
+       return ao_log_single_read(0);
+}
+
+static void
+ao_log_single_query(void)
+{
+       printf("Logging enabled: %d\n", ao_log_running);
+       printf("Log start: %ld\n", ao_log_start_pos);
+       printf("Log cur: %ld\n", ao_log_current_pos);
+       printf("Log end: %ld\n", ao_log_end_pos);
+       ao_log_single_extra_query();
+}
+
+const struct ao_cmds ao_log_single_cmds[] = {
+       { ao_log_single_set,    "L <0 off, 1 on>\0Set logging" },
+       { ao_log_single_list,   "l\0List stored logs" },
+       { ao_log_single_delete, "d 1\0Delete all stored logs" },
+       { ao_log_single_query, "q\0Query log status" },
+       { 0,    NULL },
+};
+
+void
+ao_log_single_init(void)
+{
+       ao_log_running = 0;
+
+       ao_cmd_register(&ao_log_single_cmds[0]);
+
+       ao_add_task(&ao_log_single_task, ao_log_single, "log");
+}
diff --git a/src/core/ao_log_telem.c b/src/core/ao_log_telem.c
new file mode 100644 (file)
index 0000000..18ab85d
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_flight.h>
+#include <ao_sample.h>
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELEMETRY;
+
+static __data uint8_t                  ao_log_monitor_pos;
+__pdata enum ao_flight_state           ao_flight_state;
+__pdata int16_t                                ao_max_height;  /* max of ao_height */
+__pdata int16_t                                sense_d, sense_m;
+__pdata uint8_t                                ao_igniter_present;
+
+static void
+ao_log_telem_track() {
+       if (ao_monitoring == sizeof (union ao_telemetry_all)) {
+               switch (ao_log_single_write_data.telemetry.generic.type) {
+               case AO_TELEMETRY_SENSOR_TELEMETRUM:
+               case AO_TELEMETRY_SENSOR_TELEMINI:
+                       /* fall through ... */
+               case AO_TELEMETRY_SENSOR_TELENANO:
+                       if (ao_log_single_write_data.telemetry.generic.type == AO_TELEMETRY_SENSOR_TELENANO) {
+                               ao_igniter_present = 0;
+                       } else {
+                               sense_d = ao_log_single_write_data.telemetry.sensor.sense_d;
+                               sense_m = ao_log_single_write_data.telemetry.sensor.sense_m;
+                               ao_igniter_present = 1;
+                       }
+                       if (ao_log_single_write_data.telemetry.sensor.height > ao_max_height) {
+                               ao_max_height = ao_log_single_write_data.telemetry.sensor.height;
+                       }
+                       if (ao_log_single_write_data.telemetry.sensor.state != ao_flight_state) {
+                               ao_flight_state = ao_log_single_write_data.telemetry.sensor.state;
+                               if (ao_flight_state == ao_flight_pad)
+                                       ao_max_height = 0;
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+               }
+       }
+}
+
+enum ao_igniter_status
+ao_igniter_status(enum ao_igniter igniter)
+{
+       int16_t value;
+
+       switch (igniter) {
+       case ao_igniter_drogue:
+               value = sense_d;
+               break;
+       case ao_igniter_main:
+               value = sense_m;
+               break;
+       default:
+               value = 0;
+               break;
+       }
+       if (value < AO_IGNITER_OPEN)
+               return ao_igniter_open;
+       else if (value > AO_IGNITER_CLOSED)
+               return ao_igniter_ready;
+       else
+               return ao_igniter_unknown;
+}
+
+void
+ao_log_single(void)
+{
+       ao_storage_setup();
+
+       /* This can take a while, so let the rest
+        * of the system finish booting before we start
+        */
+       ao_delay(AO_SEC_TO_TICKS(2));
+
+       ao_log_running = 1;
+       ao_log_single_restart();
+       ao_flight_state = ao_flight_startup;
+       ao_monitor_set(sizeof(struct ao_telemetry_generic));
+
+       for (;;) {
+               while (!ao_log_running)
+                       ao_sleep(&ao_log_running);
+
+               ao_log_monitor_pos = ao_monitor_head;
+               while (ao_log_running) {
+                       /* Write samples to EEPROM */
+                       while (ao_log_monitor_pos != ao_monitor_head) {
+                               memcpy(&ao_log_single_write_data.telemetry,
+                                      &ao_monitor_ring[ao_log_monitor_pos],
+                                      AO_LOG_SINGLE_SIZE);
+                               ao_log_single_write();
+                               ao_log_monitor_pos = ao_monitor_ring_next(ao_log_monitor_pos);
+                               ao_log_telem_track();
+                       }
+                       /* Wait for more telemetry data to arrive */
+                       ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
+               }
+       }
+}
+
+void
+ao_log_single_list(void)
+{
+       if (ao_log_current_pos != 0)
+               printf("flight 1 start %x end %x\n",
+                      0,
+                      (uint16_t) ((ao_log_current_pos + 0xff) >> 8));
+       printf ("done\n");
+}
+
+void
+ao_log_single_extra_query(void)
+{
+}
diff --git a/src/core/ao_log_telescience.c b/src/core/ao_log_telescience.c
new file mode 100644 (file)
index 0000000..002a10b
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_product.h"
+#include "ao_log.h"
+#include "ao_companion.h"
+
+static uint8_t ao_log_data_pos;
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TELESCIENCE;
+
+static void
+ao_log_telescience_csum(void) __reentrant
+{
+       __xdata uint8_t *b = ao_log_single_write_data.bytes;
+       uint8_t sum = 0x5a;
+       uint8_t i;
+
+       ao_log_single_write_data.telescience.csum = 0;
+       for (i = 0; i < sizeof (struct ao_log_telescience); i++)
+               sum += *b++;
+       ao_log_single_write_data.telescience.csum = -sum;
+}
+
+void
+ao_log_single(void)
+{
+       ao_storage_setup();
+
+       /* This can take a while, so let the rest
+        * of the system finish booting before we start
+        */
+       ao_delay(AO_SEC_TO_TICKS(10));
+
+       ao_log_single_restart();
+       for (;;) {
+               while (!ao_log_running)
+                       ao_sleep(&ao_log_running);
+
+               ao_log_start_pos = ao_log_current_pos;
+               ao_log_single_write_data.telescience.type = AO_LOG_TELESCIENCE_START;
+               ao_log_single_write_data.telescience.tick = ao_time();
+               ao_log_single_write_data.telescience.adc[0] = ao_companion_command.serial;
+               ao_log_single_write_data.telescience.adc[1] = ao_companion_command.flight;
+               ao_log_telescience_csum();
+               ao_log_single_write();
+               /* Write the whole contents of the ring to the log
+                * when starting up.
+                */
+               ao_log_data_pos = ao_data_ring_next(ao_data_head);
+               ao_log_single_write_data.telescience.type = AO_LOG_TELESCIENCE_DATA;
+               while (ao_log_running) {
+                       /* Write samples to EEPROM */
+                       while (ao_log_data_pos != ao_data_head) {
+                               ao_log_single_write_data.telescience.tick = ao_data_ring[ao_log_data_pos].tick;
+                               memcpy(&ao_log_single_write_data.telescience.adc, (void *) ao_data_ring[ao_log_data_pos].adc.adc,
+                                      AO_LOG_TELESCIENCE_NUM_ADC * sizeof (uint16_t));
+                               ao_log_telescience_csum();
+                               ao_log_single_write();
+                               ao_log_data_pos = ao_data_ring_next(ao_log_data_pos);
+                       }
+                       /* Wait for more ADC data to arrive */
+                       ao_sleep((void *) &ao_data_head);
+               }
+               memset(&ao_log_single_write_data.telescience.adc, '\0', sizeof (ao_log_single_write_data.telescience.adc));
+       }
+}
+
+void
+ao_log_single_list(void)
+{
+       uint32_t        pos;
+       uint32_t        start = 0;
+       uint8_t         flight = 0;
+
+       for (pos = 0; ; pos += sizeof (struct ao_log_telescience)) {
+               if (pos >= ao_storage_config ||
+                   !ao_log_single_read(pos) ||
+                   ao_log_single_read_data.telescience.type == AO_LOG_TELESCIENCE_START)
+               {
+                       if (pos != start) {
+                               printf("flight %d start %x end %x\n",
+                                      flight,
+                                      (uint16_t) (start >> 8),
+                                      (uint16_t) ((pos + 0xff) >> 8)); flush();
+                       }
+                       if (ao_log_single_read_data.telescience.type != AO_LOG_TELESCIENCE_START)
+                               break;
+                       start = pos;
+                       flight++;
+               }
+       }
+       printf ("done\n");
+}
+
+void
+ao_log_single_extra_query(void)
+{
+       printf("log data tick: %04x\n", ao_log_single_write_data.telescience.tick);
+       printf("TM data tick: %04x\n", ao_log_single_write_data.telescience.tm_tick);
+       printf("TM state: %d\n", ao_log_single_write_data.telescience.tm_state);
+       printf("TM serial: %d\n", ao_companion_command.serial);
+       printf("TM flight: %d\n", ao_companion_command.flight);
+}
diff --git a/src/core/ao_log_tiny.c b/src/core/ao_log_tiny.c
new file mode 100644 (file)
index 0000000..492658e
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static __data uint16_t ao_log_tiny_interval;
+
+#define AO_LOG_TINY_INTERVAL_DEFAULT   AO_MS_TO_TICKS(1000)
+#if USE_FAST_ASCENT_LOG
+#define AO_LOG_TINY_INTERVAL_ASCENT    AO_MS_TO_TICKS(100)
+#define AO_PAD_RING    8
+#else
+#define AO_LOG_TINY_INTERVAL_ASCENT    AO_LOG_TINY_INTERVAL_DEFAULT
+#define AO_PAD_RING    2
+#endif
+
+__code uint8_t ao_log_format = AO_LOG_FORMAT_TINY;
+
+void
+ao_log_tiny_set_interval(uint16_t ticks)
+{
+       ao_log_tiny_interval = ticks;
+}
+
+
+static void ao_log_tiny_data(uint16_t d)
+{
+       if (ao_log_current_pos >= ao_log_end_pos && ao_log_running)
+               ao_log_stop();
+       if (ao_log_running) {
+               ao_storage_write(ao_log_current_pos, DATA_TO_XDATA(&d), 2);
+               ao_log_current_pos += 2;
+       }
+}
+
+static __xdata uint16_t ao_log_pad_ring[AO_PAD_RING];
+static __pdata uint8_t ao_log_pad_ring_pos;
+
+#define ao_pad_ring_next(n)    (((n) + 1) & (AO_PAD_RING - 1))
+
+static void ao_log_tiny_queue(uint16_t d)
+{
+       ao_log_pad_ring[ao_log_pad_ring_pos] = d;
+       ao_log_pad_ring_pos = ao_pad_ring_next(ao_log_pad_ring_pos);
+}
+
+static void ao_log_tiny_start(void)
+{
+       uint8_t         p;
+       uint16_t        d;
+
+       ao_log_tiny_data(ao_flight_number);
+       ao_log_tiny_data(ao_ground_pres);
+       p = ao_log_pad_ring_pos;
+       do {
+               d = ao_log_pad_ring[p];
+               /*
+                * ignore unwritten slots
+                */
+               if (d)
+                       ao_log_tiny_data(d);
+               p = ao_pad_ring_next(p);
+       } while (p != ao_log_pad_ring_pos);
+}
+
+void
+ao_log(void)
+{
+       uint16_t                last_time;
+       uint16_t                now;
+       enum ao_flight_state    ao_log_tiny_state;
+       int32_t                 sum;
+       int16_t                 count;
+       uint8_t                 ao_log_data;
+       uint8_t                 ao_log_started = 0;
+
+       ao_storage_setup();
+
+       ao_log_scan();
+
+       ao_log_tiny_state = ao_flight_invalid;
+       ao_log_tiny_interval = AO_LOG_TINY_INTERVAL_ASCENT;
+       sum = 0;
+       count = 0;
+       ao_log_data = ao_sample_data;
+       last_time = ao_time();
+       for (;;) {
+
+               /*
+                * Add in pending sample data
+                */
+               ao_sleep(DATA_TO_XDATA(&ao_sample_data));
+               while (ao_log_data != ao_sample_data) {
+                       sum += ao_data_ring[ao_log_data].adc.pres;
+                       count++;
+                       ao_log_data = ao_data_ring_next(ao_log_data);
+               }
+               if (ao_log_running) {
+                       if (!ao_log_started) {
+                               ao_log_tiny_start();
+                               ao_log_started = 1;
+                       }
+                       if (ao_flight_state != ao_log_tiny_state) {
+                               ao_log_tiny_data(ao_flight_state | 0x8000);
+                               ao_log_tiny_state = ao_flight_state;
+                               ao_log_tiny_interval = AO_LOG_TINY_INTERVAL_DEFAULT;
+#if AO_LOG_TINY_INTERVAL_ASCENT != AO_LOG_TINY_INTERVAL_DEFAULT
+                               if (ao_log_tiny_state <= ao_flight_coast)
+                                       ao_log_tiny_interval = AO_LOG_TINY_INTERVAL_ASCENT;
+#endif
+                               if (ao_log_tiny_state == ao_flight_landed)
+                                       ao_log_stop();
+                       }
+               }
+
+               /* Stop logging when told to */
+               if (!ao_log_running && ao_log_started)
+                       ao_exit();
+
+               /*
+                * Write out the sample when finished
+                */
+               now = ao_time();
+               if ((int16_t) (now - (last_time + ao_log_tiny_interval)) >= 0 && count) {
+                       count = sum / count;
+                       if (ao_log_started)
+                               ao_log_tiny_data(count);
+                       else
+                               ao_log_tiny_queue(count);
+                       sum = 0;
+                       count = 0;
+                       last_time = now;
+               }
+       }
+}
+
+uint16_t
+ao_log_flight(uint8_t slot)
+{
+       static __xdata uint16_t flight;
+
+       (void) slot;
+       ao_storage_read(0, &flight, 2);
+       if (flight == 0xffff)
+               flight = 0;
+       return flight;
+}
diff --git a/src/core/ao_monitor.c b/src/core/ao_monitor.c
new file mode 100644 (file)
index 0000000..5876bef
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_telem.h"
+#include "ao_flight.h"
+
+#if !HAS_MONITOR
+#error Must define HAS_MONITOR to 1
+#endif
+
+#ifndef LEGACY_MONITOR
+#error Must define LEGACY_MONITOR
+#endif
+
+#ifndef HAS_MONITOR_PUT
+#define HAS_MONITOR_PUT 1
+#endif
+
+#ifndef AO_MONITOR_LED
+#error Must define AO_MONITOR_LED
+#endif
+
+__data uint8_t ao_monitoring;
+static __data uint8_t ao_monitor_disabled;
+static __data uint8_t ao_internal_monitoring;
+static __data uint8_t ao_external_monitoring;
+
+__xdata union ao_monitor ao_monitor_ring[AO_MONITOR_RING];
+
+__data uint8_t ao_monitor_head;
+
+static void
+_ao_monitor_adjust(void)
+{
+       if (ao_monitoring)
+               ao_radio_recv_abort();
+       if (ao_monitor_disabled)
+               ao_monitoring = 0;
+       else {
+               if (ao_external_monitoring)
+                       ao_monitoring = ao_external_monitoring;
+               else
+                       ao_monitoring = ao_internal_monitoring;
+       }
+       ao_wakeup(DATA_TO_XDATA(&ao_monitoring));
+}
+
+void
+ao_monitor_get(void)
+{
+       uint8_t size;
+
+       for (;;) {
+               switch (ao_monitoring) {
+               case 0:
+                       ao_sleep(DATA_TO_XDATA(&ao_monitoring));
+                       continue;
+#if LEGACY_MONITOR
+               case AO_MONITORING_ORIG:
+                       size = sizeof (struct ao_telemetry_orig_recv);
+                       break;
+#endif
+               default:
+                       if (ao_monitoring > AO_MAX_TELEMETRY)
+                               ao_monitoring = AO_MAX_TELEMETRY;
+                       size = ao_monitoring;
+                       break;
+               }
+               if (!ao_radio_recv(&ao_monitor_ring[ao_monitor_head], size + 2))
+                       continue;
+               ao_monitor_head = ao_monitor_ring_next(ao_monitor_head);
+               ao_wakeup(DATA_TO_XDATA(&ao_monitor_head));
+       }
+}
+
+#if AO_MONITOR_LED
+__xdata struct ao_task ao_monitor_blink_task;
+
+void
+ao_monitor_blink(void)
+{
+       for (;;) {
+               ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
+               ao_led_for(AO_MONITOR_LED, AO_MS_TO_TICKS(100));
+       }
+}
+#endif
+
+#if HAS_MONITOR_PUT
+
+static const char xdigit[16] = {
+       '0', '1', '2', '3', '4', '5', '6', '7',
+       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+#define hex(c) do { putchar(xdigit[(c) >> 4]); putchar(xdigit[(c)&0xf]); } while (0)
+
+void
+ao_monitor_put(void)
+{
+#if LEGACY_MONITOR
+       __xdata char callsign[AO_MAX_CALLSIGN+1];
+       int16_t rssi;
+#endif
+       uint8_t ao_monitor_tail;
+       uint8_t state;
+       uint8_t sum, byte;
+       __xdata union ao_monitor        *m;
+
+#define recv_raw       ((m->raw))
+#define recv_orig      ((m->orig))
+#define recv_tiny      ((m->tiny))
+
+       ao_monitor_tail = ao_monitor_head;
+       for (;;) {
+               while (!ao_external_monitoring)
+                       ao_sleep(DATA_TO_XDATA(&ao_external_monitoring));
+               while (ao_monitor_tail == ao_monitor_head && ao_external_monitoring)
+                       ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
+               if (!ao_external_monitoring)
+                       continue;
+               m = &ao_monitor_ring[ao_monitor_tail];
+               ao_monitor_tail = ao_monitor_ring_next(ao_monitor_tail);
+               switch (ao_monitoring) {
+               case 0:
+                       break;
+#if LEGACY_MONITOR
+               case AO_MONITORING_ORIG:
+                       state = recv_orig.telemetry_orig.flight_state;
+
+                       rssi = (int16_t) AO_RSSI_FROM_RADIO(recv_orig.rssi);
+                       ao_xmemcpy(callsign, recv_orig.telemetry_orig.callsign, AO_MAX_CALLSIGN);
+                       if (state > ao_flight_invalid)
+                               state = ao_flight_invalid;
+                       if (recv_orig.status & PKT_APPEND_STATUS_1_CRC_OK) {
+
+                               /* General header fields */
+                               printf(AO_TELEM_VERSION " %d "
+                                      AO_TELEM_CALL " %s "
+                                      AO_TELEM_SERIAL " %d "
+                                      AO_TELEM_FLIGHT " %d "
+                                      AO_TELEM_RSSI " %d "
+                                      AO_TELEM_STATE " %s "
+                                      AO_TELEM_TICK " %d ",
+                                      AO_TELEMETRY_VERSION,
+                                      callsign,
+                                      recv_orig.telemetry_orig.serial,
+                                      recv_orig.telemetry_orig.flight,
+                                      rssi,
+                                      ao_state_names[state],
+                                      recv_orig.telemetry_orig.adc.tick);
+
+                               /* Raw sensor values */
+                               printf(AO_TELEM_RAW_ACCEL " %d "
+                                      AO_TELEM_RAW_BARO " %d "
+                                      AO_TELEM_RAW_THERMO " %d "
+                                      AO_TELEM_RAW_BATT " %d "
+                                      AO_TELEM_RAW_DROGUE " %d "
+                                      AO_TELEM_RAW_MAIN " %d ",
+                                      recv_orig.telemetry_orig.adc.accel,
+                                      recv_orig.telemetry_orig.adc.pres,
+                                      recv_orig.telemetry_orig.adc.temp,
+                                      recv_orig.telemetry_orig.adc.v_batt,
+                                      recv_orig.telemetry_orig.adc.sense_d,
+                                      recv_orig.telemetry_orig.adc.sense_m);
+
+                               /* Sensor calibration values */
+                               printf(AO_TELEM_CAL_ACCEL_GROUND " %d "
+                                      AO_TELEM_CAL_BARO_GROUND " %d "
+                                      AO_TELEM_CAL_ACCEL_PLUS " %d "
+                                      AO_TELEM_CAL_ACCEL_MINUS " %d ",
+                                      recv_orig.telemetry_orig.ground_accel,
+                                      recv_orig.telemetry_orig.ground_pres,
+                                      recv_orig.telemetry_orig.accel_plus_g,
+                                      recv_orig.telemetry_orig.accel_minus_g);
+
+                               if (recv_orig.telemetry_orig.u.k.unused == 0x8000) {
+                                       /* Kalman state values */
+                                       printf(AO_TELEM_KALMAN_HEIGHT " %d "
+                                              AO_TELEM_KALMAN_SPEED " %d "
+                                              AO_TELEM_KALMAN_ACCEL " %d ",
+                                              recv_orig.telemetry_orig.height,
+                                              recv_orig.telemetry_orig.u.k.speed,
+                                              recv_orig.telemetry_orig.accel);
+                               } else {
+                                       /* Ad-hoc flight values */
+                                       printf(AO_TELEM_ADHOC_ACCEL " %d "
+                                              AO_TELEM_ADHOC_SPEED " %ld "
+                                              AO_TELEM_ADHOC_BARO " %d ",
+                                              recv_orig.telemetry_orig.accel,
+                                              recv_orig.telemetry_orig.u.flight_vel,
+                                              recv_orig.telemetry_orig.height);
+                               }
+                               ao_gps_print(&recv_orig.telemetry_orig.gps);
+                               ao_gps_tracking_print(&recv_orig.telemetry_orig.gps_tracking);
+                               putchar('\n');
+#if HAS_RSSI
+                               ao_rssi_set(rssi);
+#endif
+                       } else {
+                               printf("CRC INVALID RSSI %3d\n", rssi);
+                       }
+                       break;
+#endif /* LEGACY_MONITOR */
+               default:
+#if AO_PROFILE
+               {
+                       extern uint32_t ao_rx_start_tick, ao_rx_packet_tick, ao_rx_done_tick, ao_rx_last_done_tick;
+                       extern uint32_t ao_fec_decode_start, ao_fec_decode_end;
+
+                       printf ("between packet: %d\n", ao_rx_start_tick - ao_rx_last_done_tick);
+                       printf ("receive start delay: %d\n", ao_rx_packet_tick - ao_rx_start_tick);
+                       printf ("decode time: %d\n", ao_fec_decode_end - ao_fec_decode_start);
+                       printf ("rx cleanup: %d\n", ao_rx_done_tick - ao_fec_decode_end);
+               }
+#endif
+                       printf("TELEM ");
+                       hex((uint8_t) (ao_monitoring + 2));
+                       sum = 0x5a;
+                       for (state = 0; state < ao_monitoring + 2; state++) {
+                               byte = recv_raw.packet[state];
+                               sum += byte;
+                               hex(byte);
+                       }
+                       hex(sum);
+                       putchar ('\n');
+#if HAS_RSSI
+                       if (recv_raw.packet[ao_monitoring + 1] & PKT_APPEND_STATUS_1_CRC_OK) {
+                               rssi = AO_RSSI_FROM_RADIO(recv_raw.packet[ao_monitoring]);
+                               ao_rssi_set(rssi);
+                       }
+#endif
+                       break;
+               }
+               ao_usb_flush();
+       }
+}
+
+__xdata struct ao_task ao_monitor_put_task;
+#endif
+
+__xdata struct ao_task ao_monitor_get_task;
+
+void
+ao_monitor_set(uint8_t monitoring)
+{
+       ao_internal_monitoring = monitoring;
+       _ao_monitor_adjust();
+}
+
+void
+ao_monitor_disable(void)
+{
+       ++ao_monitor_disabled;
+       _ao_monitor_adjust();
+}
+
+void
+ao_monitor_enable(void)
+{
+       --ao_monitor_disabled;
+       _ao_monitor_adjust();
+}
+
+#if HAS_MONITOR_PUT
+static void
+set_monitor(void)
+{
+       ao_cmd_hex();
+       ao_external_monitoring = ao_cmd_lex_i;
+       ao_wakeup(DATA_TO_XDATA(&ao_external_monitoring));
+       ao_wakeup(DATA_TO_XDATA(&ao_monitor_head));
+       _ao_monitor_adjust();
+}
+
+__code struct ao_cmds ao_monitor_cmds[] = {
+       { set_monitor,  "m <0 off, 1 old, 20 std>\0Set radio monitoring" },
+       { 0,    NULL },
+};
+#endif
+
+void
+ao_monitor_init(void) __reentrant
+{
+#if HAS_MONITOR_PUT
+       ao_cmd_register(&ao_monitor_cmds[0]);
+       ao_add_task(&ao_monitor_put_task, ao_monitor_put, "monitor_put");
+#endif
+       ao_add_task(&ao_monitor_get_task, ao_monitor_get, "monitor_get");
+#if AO_MONITOR_LED
+       ao_add_task(&ao_monitor_blink_task, ao_monitor_blink, "monitor_blink");
+#endif
+}
diff --git a/src/core/ao_mutex.c b/src/core/ao_mutex.c
new file mode 100644 (file)
index 0000000..c82a7d5
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+ao_mutex_get(__xdata uint8_t *mutex) __reentrant
+{
+       if (*mutex == ao_cur_task->task_id)
+               ao_panic(AO_PANIC_MUTEX);
+       __critical {
+               while (*mutex)
+                       ao_sleep(mutex);
+               *mutex = ao_cur_task->task_id;
+       }
+}
+
+void
+ao_mutex_put(__xdata uint8_t *mutex) __reentrant
+{
+       if (*mutex != ao_cur_task->task_id)
+               ao_panic(AO_PANIC_MUTEX);
+       __critical {
+               *mutex = 0;
+               ao_wakeup(mutex);
+       }
+}
diff --git a/src/core/ao_packet.h b/src/core/ao_packet.h
new file mode 100644 (file)
index 0000000..f232a87
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PACKET_H_
+#define _AO_PACKET_H_
+
+/*
+ * ao_packet.c
+ *
+ * Packet-based command interface
+ */
+
+#define AO_PACKET_MAX          64
+#define AO_PACKET_SYN          (uint8_t) 0xff
+
+struct ao_packet {
+       uint8_t         addr;
+       uint8_t         len;
+       uint8_t         seq;
+       uint8_t         ack;
+       uint8_t         d[AO_PACKET_MAX];
+       uint8_t         callsign[AO_MAX_CALLSIGN];
+};
+
+struct ao_packet_recv {
+       struct ao_packet        packet;
+       int8_t                  rssi;
+       uint8_t                 status;
+};
+
+extern __xdata struct ao_packet_recv ao_rx_packet;
+extern __xdata struct ao_packet ao_tx_packet;
+extern __xdata struct ao_task  ao_packet_task;
+extern __xdata uint8_t ao_packet_enable;
+extern __xdata uint8_t ao_packet_master_sleeping;
+extern __pdata uint8_t ao_packet_rx_len, ao_packet_rx_used, ao_packet_tx_used;
+
+void
+ao_packet_send(void);
+
+uint8_t
+ao_packet_recv(void);
+
+void
+ao_packet_flush(void);
+
+void
+ao_packet_putchar(char c) __reentrant;
+
+char
+ao_packet_pollchar(void) __critical;
+
+#if PACKET_HAS_MASTER
+/* ao_packet_master.c */
+
+extern __xdata uint8_t ao_packet_last_rssi;
+
+void
+ao_packet_master_init(void);
+#endif
+
+#if PACKET_HAS_SLAVE
+/* ao_packet_slave.c */
+
+void
+ao_packet_slave_start(void);
+
+void
+ao_packet_slave_stop(void);
+
+void
+ao_packet_slave_init(uint8_t enable);
+
+#endif
+
+#endif /* _AO_PACKET_H_ */
diff --git a/src/core/ao_panic.c b/src/core/ao_panic.c
new file mode 100644 (file)
index 0000000..5243304
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#ifndef HAS_BEEP
+#error Please define HAS_BEEP
+#endif
+
+#if !HAS_BEEP
+#define ao_beep(x)
+#endif
+#if !LEDS_AVAILABLE
+#define ao_led_on(x)
+#define ao_led_off(x)
+#endif
+
+static void
+ao_panic_delay(uint8_t n)
+{
+       uint8_t i = 0, j = 0;
+
+       while (n--)
+               while (--j)
+                       while (--i)
+                               ao_arch_nop();
+}
+
+void
+ao_panic(uint8_t reason)
+{
+       uint8_t n;
+
+#if LOW_LEVEL_DEBUG
+       ao_cur_task = NULL;
+       printf ("panic %d\n", reason);
+#endif
+       __critical for (;;) {
+               ao_panic_delay(20);
+               for (n = 0; n < 5; n++) {
+                       ao_led_on(AO_LED_RED);
+                       ao_beep(AO_BEEP_HIGH);
+                       ao_panic_delay(1);
+                       ao_led_off(AO_LED_RED);
+                       ao_beep(AO_BEEP_LOW);
+                       ao_panic_delay(1);
+               }
+               ao_beep(AO_BEEP_OFF);
+               ao_panic_delay(2);
+
+#ifdef SDCC
+#pragma disable_warning 126
+#endif
+               if (reason & 0x40) {
+                       ao_led_on(AO_LED_RED);
+                       ao_beep(AO_BEEP_HIGH);
+                       ao_panic_delay(40);
+                       ao_led_off(AO_LED_RED);
+                       ao_beep(AO_BEEP_OFF);
+                       ao_panic_delay(10);
+               }
+               for (n = 0; n < (reason & 0x3f); n++) {
+                       ao_led_on(AO_LED_RED);
+                       ao_beep(AO_BEEP_MID);
+                       ao_panic_delay(10);
+                       ao_led_off(AO_LED_RED);
+                       ao_beep(AO_BEEP_OFF);
+                       ao_panic_delay(10);
+               }
+       }
+}
diff --git a/src/core/ao_product.c b/src/core/ao_product.c
new file mode 100644 (file)
index 0000000..ec91b97
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_product.h"
+
+/* Defines which mark this particular AltOS product */
+
+const char ao_version[AO_MAX_VERSION] = AO_iVersion_STRING;
+const char ao_manufacturer[] = AO_iManufacturer_STRING;
+const char ao_product[] = AO_iProduct_STRING;
+
+#define LE_WORD(x)    ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8))
+
+#if HAS_USB
+#include "ao_usb.h"
+/* USB descriptors in one giant block of bytes */
+AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =
+{
+       /* Device descriptor */
+       0x12,
+       AO_USB_DESC_DEVICE,
+       LE_WORD(0x0110),        /*  bcdUSB */
+       0x02,                   /*  bDeviceClass */
+       0x00,                   /*  bDeviceSubClass */
+       0x00,                   /*  bDeviceProtocol */
+       AO_USB_CONTROL_SIZE,    /*  bMaxPacketSize */
+       LE_WORD(0xFFFE),        /*  idVendor */
+       LE_WORD(AO_idProduct_NUMBER),   /*  idProduct */
+       LE_WORD(0x0100),        /*  bcdDevice */
+       0x01,                   /*  iManufacturer */
+       0x02,                   /*  iProduct */
+       0x03,                   /*  iSerialNumber */
+       0x01,                   /*  bNumConfigurations */
+
+       /* Configuration descriptor */
+       0x09,
+       AO_USB_DESC_CONFIGURATION,
+       LE_WORD(67),            /*  wTotalLength */
+       0x02,                   /*  bNumInterfaces */
+       0x01,                   /*  bConfigurationValue */
+       0x00,                   /*  iConfiguration */
+       0xC0,                   /*  bmAttributes */
+       0x32,                   /*  bMaxPower */
+
+       /* Control class interface */
+       0x09,
+       AO_USB_DESC_INTERFACE,
+       0x00,                   /*  bInterfaceNumber */
+       0x00,                   /*  bAlternateSetting */
+       0x01,                   /*  bNumEndPoints */
+       0x02,                   /*  bInterfaceClass */
+       0x02,                   /*  bInterfaceSubClass */
+       0x01,                   /*  bInterfaceProtocol, linux requires value of 1 for the cdc_acm module */
+       0x00,                   /*  iInterface */
+
+       /* Header functional descriptor */
+       0x05,
+       AO_USB_CS_INTERFACE,
+       0x00,                   /*  bDescriptor SubType Header */
+       LE_WORD(0x0110),        /*  CDC version 1.1 */
+
+       /* Call management functional descriptor */
+       0x05,
+       AO_USB_CS_INTERFACE,
+       0x01,                   /* bDescriptor SubType Call Management */
+       0x01,                   /* bmCapabilities = device handles call management */
+       0x01,                   /* bDataInterface call management interface number */
+
+       /* ACM functional descriptor */
+       0x04,
+       AO_USB_CS_INTERFACE,
+       0x02,                   /* bDescriptor SubType Abstract Control Management */
+       0x02,                   /* bmCapabilities = D1 (Set_line_Coding, Set_Control_Line_State, Get_Line_Coding and Serial_State) */
+
+       /* Union functional descriptor */
+       0x05,
+       AO_USB_CS_INTERFACE,
+       0x06,                   /* bDescriptor SubType Union Functional descriptor */
+       0x00,                   /* bMasterInterface */
+       0x01,                   /* bSlaveInterface0 */
+
+       /* Notification EP */
+       0x07,
+       AO_USB_DESC_ENDPOINT,
+       AO_USB_INT_EP|0x80,     /* bEndpointAddress */
+       0x03,                   /* bmAttributes = intr */
+       LE_WORD(8),             /* wMaxPacketSize */
+       0xff,                   /* bInterval */
+
+       /* Data class interface descriptor */
+       0x09,
+       AO_USB_DESC_INTERFACE,
+       0x01,                   /* bInterfaceNumber */
+       0x00,                   /* bAlternateSetting */
+       0x02,                   /* bNumEndPoints */
+       0x0A,                   /* bInterfaceClass = data */
+       0x00,                   /* bInterfaceSubClass */
+       0x00,                   /* bInterfaceProtocol */
+       0x00,                   /* iInterface */
+
+       /* Data EP OUT */
+       0x07,
+       AO_USB_DESC_ENDPOINT,
+       AO_USB_OUT_EP,          /* bEndpointAddress */
+       0x02,                   /* bmAttributes = bulk */
+       LE_WORD(AO_USB_OUT_SIZE),/* wMaxPacketSize */
+       0x00,                   /* bInterval */
+
+       /* Data EP in */
+       0x07,
+       AO_USB_DESC_ENDPOINT,
+       AO_USB_IN_EP|0x80,      /* bEndpointAddress */
+       0x02,                   /* bmAttributes = bulk */
+       LE_WORD(AO_USB_IN_SIZE),/* wMaxPacketSize */
+       0x00,                   /* bInterval */
+
+       /* String descriptors */
+       0x04,
+       AO_USB_DESC_STRING,
+       LE_WORD(0x0409),
+
+       /* iManufacturer */
+       AO_iManufacturer_LEN,
+       AO_USB_DESC_STRING,
+       AO_iManufacturer_UCS2,
+
+       /* iProduct */
+       AO_iProduct_LEN,
+       AO_USB_DESC_STRING,
+       AO_iProduct_UCS2,
+
+       /* iSerial */
+       AO_iSerial_LEN,
+       AO_USB_DESC_STRING,
+       AO_iSerial_UCS2,
+
+       /* Terminating zero */
+       0
+};
+#endif
diff --git a/src/core/ao_pyro.c b/src/core/ao_pyro.c
new file mode 100644 (file)
index 0000000..4f37e97
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_pyro.h>
+#include <ao_sample.h>
+#include <ao_flight.h>
+
+#if IS_COMPANION
+#include <ao_companion.h>
+#define ao_accel ao_companion_command.accel
+#define ao_speed ao_companion_command.speed
+#define ao_height ao_companion_command.height
+#define ao_flight_state ao_companion_command.flight_state
+#define ao_motor_number ao_companion_command.motor_number
+#endif
+
+#define ao_lowbit(x)   ((x) & (-x))
+
+/*
+ * Given a pyro structure, figure out
+ * if the current flight state satisfies all
+ * of the requirements
+ */
+static uint8_t
+ao_pyro_ready(struct ao_pyro *pyro)
+{
+       enum ao_pyro_flag flag, flags;
+
+       flags = pyro->flags;
+       while (flags != ao_pyro_none) {
+               flag = ao_lowbit(flags);
+               flags &= ~flag;
+               switch (flag) {
+
+               case ao_pyro_accel_less:
+                       if (ao_accel <= pyro->accel_less)
+                               continue;
+                       break;
+               case ao_pyro_accel_greater:
+                       if (ao_accel >= pyro->accel_greater)
+                               continue;
+                       break;
+
+
+               case ao_pyro_speed_less:
+                       if (ao_speed <= pyro->speed_less)
+                               continue;
+                       break;
+               case ao_pyro_speed_greater:
+                       if (ao_speed >= pyro->speed_greater)
+                               continue;
+                       break;
+
+               case ao_pyro_height_less:
+                       if (ao_height <= pyro->height_less)
+                               continue;
+                       break;
+               case ao_pyro_height_greater:
+                       if (ao_height >= pyro->height_greater)
+                               continue;
+                       break;
+
+#if HAS_ORIENT
+               case ao_pyro_orient_less:
+                       if (ao_orient <= pyro->orient_less)
+                               continue;
+                       break;
+               case ao_pyro_orient_greater:
+                       if (ao_orient >= pyro->orient_greater)
+                               continue;
+                       break;
+#endif
+
+               case ao_pyro_time_less:
+                       if ((int16_t) (ao_time() - ao_boost_tick) <= pyro->time_less)
+                               continue;
+                       break;
+               case ao_pyro_time_greater:
+                       if ((int16_t) (ao_time() - ao_boost_tick) >= pyro->time_greater)
+                               continue;
+                       break;
+
+               case ao_pyro_ascending:
+                       if (ao_speed > 0)
+                               continue;
+                       break;
+               case ao_pyro_descending:
+                       if (ao_speed < 0)
+                               continue;
+                       break;
+
+               case ao_pyro_after_motor:
+                       if (ao_motor_number == pyro->motor)
+                               continue;
+                       break;
+
+               case ao_pyro_delay:
+                       /* handled separately */
+                       continue;
+
+               default:
+                       continue;
+               }
+               return FALSE;
+       }
+       return TRUE;
+}
+
+#define ao_pyro_fire_port(port, bit, pin) do { \
+               ao_gpio_set(port, bit, pin, 1); \
+               ao_delay(AO_MS_TO_TICKS(50));   \
+               ao_gpio_set(port, bit, pin, 0); \
+       } while (0)
+
+
+static void
+ao_pyro_fire(uint8_t p)
+{
+       switch (p) {
+#if AO_PYRO_NUM > 0
+       case 0: ao_pyro_fire_port(AO_PYRO_PORT_0, AO_PYRO_PIN_0, AO_PYRO_0); break;
+#endif
+#if AO_PYRO_NUM > 1
+       case 1: ao_pyro_fire_port(AO_PYRO_PORT_1, AO_PYRO_PIN_1, AO_PYRO_1); break;
+#endif
+#if AO_PYRO_NUM > 2
+       case 2: ao_pyro_fire_port(AO_PYRO_PORT_2, AO_PYRO_PIN_2, AO_PYRO_2); break;
+#endif
+#if AO_PYRO_NUM > 3
+       case 3: ao_pyro_fire_port(AO_PYRO_PORT_3, AO_PYRO_PIN_3, AO_PYRO_3); break;
+#endif
+#if AO_PYRO_NUM > 4
+       case 4: ao_pyro_fire_port(AO_PYRO_PORT_4, AO_PYRO_PIN_4, AO_PYRO_4); break;
+#endif
+#if AO_PYRO_NUM > 5
+       case 5: ao_pyro_fire_port(AO_PYRO_PORT_5, AO_PYRO_PIN_5, AO_PYRO_5); break;
+#endif
+#if AO_PYRO_NUM > 6
+       case 6: ao_pyro_fire_port(AO_PYRO_PORT_6, AO_PYRO_PIN_6, AO_PYRO_6); break;
+#endif
+#if AO_PYRO_NUM > 7
+       case 7: ao_pyro_fire_port(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7); break;
+#endif
+       default: break;
+       }
+       ao_delay(AO_MS_TO_TICKS(50));
+}
+
+uint8_t        ao_pyro_wakeup;
+
+static void
+ao_pyro(void)
+{
+       uint8_t         p;
+       struct ao_pyro  *pyro;
+
+       ao_config_get();
+       while (ao_flight_state < ao_flight_boost)
+               ao_sleep(&ao_flight_state);
+
+       for (;;) {
+               ao_alarm(AO_MS_TO_TICKS(100));
+               ao_sleep(&ao_pyro_wakeup);
+               ao_clear_alarm();
+               for (p = 0; p < AO_PYRO_NUM; p++) {
+                       pyro = &ao_config.pyro[p];
+
+                       /* Ignore igniters which have already fired
+                        */
+                       if (pyro->fired)
+                               continue;
+
+                       /* Ignore disabled igniters
+                        */
+                       if (!pyro->flags)
+                               continue;
+
+                       /* Check pyro state to see if it shoule fire
+                        */
+                       if (!pyro->delay_done) {
+                               if (!ao_pyro_ready(pyro))
+                                       continue;
+
+                               /* If there's a delay set, then remember when
+                                * it expires
+                                */
+                               if (pyro->flags & ao_pyro_delay)
+                                       pyro->delay_done = ao_time() + pyro->delay;
+                       }
+
+                       /* Check to see if we're just waiting for
+                        * the delay to expire
+                        */
+                       if (pyro->delay_done) {
+                               if ((int16_t) (ao_time() - pyro->delay_done) < 0)
+                                       continue;
+                       }
+
+                       ao_pyro_fire(p);
+               }
+       }
+}
+
+__xdata struct ao_task ao_pyro_task;
+
+#define NO_VALUE       0xff
+
+#define AO_PYRO_NAME_LEN       3
+
+#if !DISABLE_HELP
+#define ENABLE_HELP 1
+#endif
+
+#if ENABLE_HELP
+#define HELP(s)        (s)
+#else
+#define HELP(s)
+#endif
+
+const struct {
+       char                    name[AO_PYRO_NAME_LEN];
+       enum ao_pyro_flag       flag;
+       uint8_t                 offset;
+#if ENABLE_HELP
+       char                    *help;
+#endif
+} ao_pyro_values[] = {
+       { "a<", ao_pyro_accel_less,     offsetof(struct ao_pyro, accel_less), HELP("accel less (m/ss * 16)") },
+       { "a>", ao_pyro_accel_greater,  offsetof(struct ao_pyro, accel_greater), HELP("accel greater (m/ss * 16)") },
+
+       { "s<", ao_pyro_speed_less,     offsetof(struct ao_pyro, speed_less), HELP("speed less (m/s * 16)") },
+       { "s>", ao_pyro_speed_greater,  offsetof(struct ao_pyro, speed_greater), HELP("speed greater (m/s * 16)") },
+
+       { "h<", ao_pyro_height_less,    offsetof(struct ao_pyro, height_less), HELP("height less (m)") },
+       { "h>", ao_pyro_height_greater, offsetof(struct ao_pyro, height_greater), HELP("height greater (m)") },
+
+#if HAS_ORIENT
+       { "o<", ao_pyro_orient_less,    offsetof(struct ao_pyro, orient_less), HELP("orient less (deg)") },
+       { "o>", ao_pyro_orient_greater, offsetof(struct ao_pyro, orient_greater), HELP("orient greater (deg)")  },
+#endif
+
+       { "t<", ao_pyro_time_less,      offsetof(struct ao_pyro, time_less), HELP("time less (s * 100)") },
+       { "t>", ao_pyro_time_greater,   offsetof(struct ao_pyro, time_greater), HELP("time greater (s * 100)")  },
+
+       { "A", ao_pyro_ascending,       NO_VALUE, HELP("ascending") },
+       { "D", ao_pyro_descending,      NO_VALUE, HELP("descending") },
+
+       { "m", ao_pyro_after_motor,     offsetof(struct ao_pyro, motor), HELP("after motor") },
+
+       { "d", ao_pyro_delay,           offsetof(struct ao_pyro, delay), HELP("delay before firing (s * 100)") },
+       { "", ao_pyro_none,             NO_VALUE, HELP(NULL) },
+};
+
+static void
+ao_pyro_print_name(uint8_t v)
+{
+       const char *s = ao_pyro_values[v].name;
+       printf ("%s%s", s, "   " + strlen(s));
+}
+
+#if ENABLE_HELP
+static void
+ao_pyro_help(void)
+{
+       uint8_t v;
+       for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
+               ao_pyro_print_name(v);
+               if (ao_pyro_values[v].offset != NO_VALUE)
+                       printf ("<n> ");
+               else
+                       printf ("    ");
+               printf ("%s\n", ao_pyro_values[v].help);
+       }
+}
+#endif
+
+void
+ao_pyro_show(void)
+{
+       uint8_t         p;
+       uint8_t         v;
+       struct ao_pyro  *pyro;
+
+       printf ("Pyro-count: %d\n", AO_PYRO_NUM);
+       for (p = 0; p < AO_PYRO_NUM; p++) {
+               printf ("Pyro %2d: ", p);
+               pyro = &ao_config.pyro[p];
+               if (!pyro->flags) {
+                       printf ("<disabled>\n");
+                       continue;
+               }
+               for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
+                       if (!(pyro->flags & ao_pyro_values[v].flag))
+                               continue;
+                       ao_pyro_print_name(v);
+                       if (ao_pyro_values[v].offset != NO_VALUE) {
+                               int16_t value;
+
+                               value = *((int16_t *) ((char *) pyro + ao_pyro_values[v].offset));
+                               printf ("%6d ", value);
+                       } else {
+                               printf ("       ");
+                       }
+               }
+               printf ("\n");
+       }
+}
+
+void
+ao_pyro_set(void)
+{
+       uint8_t p;
+       struct ao_pyro pyro_tmp;
+       char    name[AO_PYRO_NAME_LEN];
+       uint8_t c;
+       uint8_t v;
+
+       ao_cmd_white();
+
+#if ENABLE_HELP
+       switch (ao_cmd_lex_c) {
+       case '?':
+               ao_pyro_help();
+               return;
+       }
+#endif
+
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       p = ao_cmd_lex_i;
+       if (p < 0 || AO_PYRO_NUM <= p) {
+               printf ("invalid pyro channel %d\n", p);
+               return;
+       }
+       pyro_tmp.flags = 0;
+       for (;;) {
+               ao_cmd_white();
+               if (ao_cmd_lex_c == '\n')
+                       break;
+
+               for (c = 0; c < AO_PYRO_NAME_LEN - 1; c++) {
+                       if (ao_cmd_is_white())
+                               break;
+                       name[c] = ao_cmd_lex_c;
+                       ao_cmd_lex();
+               }
+               name[c] = '\0';
+               for (v = 0; ao_pyro_values[v].flag != ao_pyro_none; v++) {
+                       if (!strcmp (ao_pyro_values[v].name, name))
+                               break;
+               }
+               if (ao_pyro_values[v].flag == ao_pyro_none) {
+                       printf ("invalid pyro field %s\n", name);
+                       ao_cmd_status = ao_cmd_syntax_error;
+                       return;
+               }
+               pyro_tmp.flags |= ao_pyro_values[v].flag;
+               if (ao_pyro_values[v].offset != NO_VALUE) {
+                       ao_cmd_decimal();
+                       if (ao_cmd_status != ao_cmd_success)
+                               return;
+                       *((int16_t *) ((char *) &pyro_tmp + ao_pyro_values[v].offset)) = ao_cmd_lex_i;
+               }
+       }
+       _ao_config_edit_start();
+       ao_config.pyro[p] = pyro_tmp;
+       _ao_config_edit_finish();
+}
+
+static void
+ao_pyro_manual(void)
+{
+       ao_cmd_white();
+       if (!ao_match_word("DoIt"))
+               return;
+       ao_cmd_white();
+       ao_cmd_decimal();
+       if (ao_cmd_lex_i < 0 || AO_PYRO_NUM <= ao_cmd_lex_i)
+               return;
+       ao_pyro_fire(ao_cmd_lex_i);
+
+}
+
+const struct ao_cmds ao_pyro_cmds[] = {
+       { ao_pyro_manual,       "P DoIt <n>\0Fire igniter" },
+       { 0, NULL }
+};
+
+void
+ao_pyro_init(void)
+{
+#if AO_PYRO_NUM > 0
+       ao_enable_output(AO_PYRO_PORT_0, AO_PYRO_PIN_0, AO_PYRO_0, 0);
+#endif
+#if AO_PYRO_NUM > 1
+       ao_enable_output(AO_PYRO_PORT_1, AO_PYRO_PIN_1, AO_PYRO_1, 0);
+#endif
+#if AO_PYRO_NUM > 2
+       ao_enable_output(AO_PYRO_PORT_2, AO_PYRO_PIN_2, AO_PYRO_2, 0);
+#endif
+#if AO_PYRO_NUM > 3
+       ao_enable_output(AO_PYRO_PORT_3, AO_PYRO_PIN_3, AO_PYRO_3, 0);
+#endif
+#if AO_PYRO_NUM > 4
+       ao_enable_output(AO_PYRO_PORT_4, AO_PYRO_PIN_4, AO_PYRO_4, 0);
+#endif
+#if AO_PYRO_NUM > 5
+       ao_enable_output(AO_PYRO_PORT_5, AO_PYRO_PIN_5, AO_PYRO_5, 0);
+#endif
+#if AO_PYRO_NUM > 6
+       ao_enable_output(AO_PYRO_PORT_6, AO_PYRO_PIN_6, AO_PYRO_6, 0);
+#endif
+#if AO_PYRO_NUM > 7
+       ao_enable_output(AO_PYRO_PORT_7, AO_PYRO_PIN_7, AO_PYRO_7, 0);
+#endif
+       ao_cmd_register(&ao_pyro_cmds[0]);
+       ao_add_task(&ao_pyro_task, ao_pyro, "pyro");
+}
diff --git a/src/core/ao_pyro.h b/src/core/ao_pyro.h
new file mode 100644 (file)
index 0000000..5deb69d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PYRO_H_
+#define _AO_PYRO_H_
+
+enum ao_pyro_flag {
+       ao_pyro_none                    = 0x00000000,
+
+       ao_pyro_accel_less              = 0x00000001,
+       ao_pyro_accel_greater           = 0x00000002,
+
+       ao_pyro_speed_less              = 0x00000004,
+       ao_pyro_speed_greater           = 0x00000008,
+
+       ao_pyro_height_less             = 0x00000010,
+       ao_pyro_height_greater          = 0x00000020,
+
+       ao_pyro_orient_less             = 0x00000040,
+       ao_pyro_orient_greater          = 0x00000080,
+
+       ao_pyro_time_less               = 0x00000100,
+       ao_pyro_time_greater            = 0x00000200,
+
+       ao_pyro_ascending               = 0x00000400,
+       ao_pyro_descending              = 0x00000800,
+
+       ao_pyro_after_motor             = 0x00001000,
+
+       ao_pyro_delay                   = 0x00002000,
+};
+
+struct ao_pyro {
+       enum ao_pyro_flag       flags;
+       int16_t                 accel_less, accel_greater;
+       int16_t                 speed_less, speed_greater;
+       int16_t                 height_less, height_greater;
+       int16_t                 orient_less, orient_greater;
+       int16_t                 time_less, time_greater;
+       int16_t                 delay;
+       int16_t                 motor;
+       uint16_t                delay_done;
+       uint8_t                 fired;
+};
+
+extern uint8_t ao_pyro_wakeup;
+
+void
+ao_pyro_set(void);
+
+void
+ao_pyro_show(void);
+
+void
+ao_pyro_init(void);
+
+#endif
diff --git a/src/core/ao_radio_cmac.c b/src/core/ao_radio_cmac.c
new file mode 100644 (file)
index 0000000..fc0ca8b
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_radio_cmac.h>
+
+static __xdata uint8_t ao_radio_cmac_mutex;
+__pdata int8_t ao_radio_cmac_rssi;
+static __xdata uint8_t cmac_data[AO_CMAC_MAX_LEN + AO_CMAC_KEY_LEN + 2 + AO_CMAC_KEY_LEN];
+static __pdata uint8_t ao_radio_cmac_len;
+
+static uint8_t
+round_len(uint8_t len)
+{
+       uint8_t rem;
+
+       /* Make sure we transfer at least one packet, and
+        * then make sure every packet is full. Note that
+        * there is no length encoded, and that the receiver
+        * must deal with any extra bytes in the packet
+        */
+       if (len < AO_CMAC_KEY_LEN)
+               len = AO_CMAC_KEY_LEN;
+       rem = len % AO_CMAC_KEY_LEN;
+       if (rem != 0)
+               len += (AO_CMAC_KEY_LEN - rem);
+       return len;
+}
+
+/*
+ * Sign and deliver the data sitting in the cmac buffer
+ */
+static void
+radio_cmac_send(uint8_t len) __reentrant
+{
+       uint8_t i;
+
+       len = round_len(len);
+       /* Make sure the AES key is loaded */
+       ao_config_get();
+
+#if HAS_MONITOR
+       ao_monitor_set(0);
+#endif
+
+       ao_mutex_get(&ao_aes_mutex);
+       ao_aes_set_mode(ao_aes_mode_cbc_mac);
+       ao_aes_set_key(ao_config.aes_key);
+       ao_aes_zero_iv();
+       for (i = 0; i < len; i += AO_CMAC_KEY_LEN) {
+               if (i + AO_CMAC_KEY_LEN < len)
+                       ao_aes_run(&cmac_data[i], NULL);
+               else
+                       ao_aes_run(&cmac_data[i], &cmac_data[len]);
+       }
+       ao_mutex_put(&ao_aes_mutex);
+
+       ao_radio_send(cmac_data, len + AO_CMAC_KEY_LEN);
+}
+
+/*
+ * Receive and validate an incoming packet
+ */
+
+static int8_t
+radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant
+{
+       uint8_t i;
+
+       len = round_len(len);
+#if HAS_MONITOR
+       ao_monitor_set(0);
+#endif
+       if (timeout)
+               ao_alarm(timeout);
+
+       i = ao_radio_recv(cmac_data, len + AO_CMAC_KEY_LEN + 2);
+       ao_clear_alarm();
+
+       if (!i) {
+               ao_radio_cmac_rssi = 0;
+               return AO_RADIO_CMAC_TIMEOUT;
+       }
+
+       ao_radio_cmac_rssi = (int8_t) (((int8_t) cmac_data[len + AO_CMAC_KEY_LEN]) >> 1) - 74;
+       if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & AO_RADIO_STATUS_CRC_OK))
+               return AO_RADIO_CMAC_CRC_ERROR;
+
+       ao_config_get();
+
+       /* Compute the packet signature
+        */
+       ao_mutex_get(&ao_aes_mutex);
+       ao_aes_set_mode(ao_aes_mode_cbc_mac);
+       ao_aes_set_key(ao_config.aes_key);
+       ao_aes_zero_iv();
+       for (i = 0; i < len; i += AO_CMAC_KEY_LEN) {
+               if (i + AO_CMAC_KEY_LEN < len)
+                       ao_aes_run(&cmac_data[i], NULL);
+               else
+                       ao_aes_run(&cmac_data[i], &cmac_data[len + AO_CMAC_KEY_LEN + 2]);
+       }
+       ao_mutex_put(&ao_aes_mutex);
+
+       /* Check the packet signature against the signature provided
+        * over the link
+        */
+        
+       if (memcmp(&cmac_data[len],
+                  &cmac_data[len + AO_CMAC_KEY_LEN + 2],
+                  AO_CMAC_KEY_LEN) != 0) {
+               return AO_RADIO_CMAC_MAC_ERROR;
+       }
+
+       return AO_RADIO_CMAC_OK;
+}
+
+int8_t
+ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant
+{
+       if (len > AO_CMAC_MAX_LEN)
+               return AO_RADIO_CMAC_LEN_ERROR;
+       ao_mutex_get(&ao_radio_cmac_mutex);
+       ao_xmemcpy(cmac_data, packet, len);
+#if AO_LED_TX
+       ao_led_on(AO_LED_TX);
+#endif
+       radio_cmac_send(len);
+#if AO_LED_TX
+       ao_led_off(AO_LED_TX);
+#endif
+       ao_mutex_put(&ao_radio_cmac_mutex);
+       return AO_RADIO_CMAC_OK;
+}
+
+int8_t
+ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant
+{
+       uint8_t i;
+       if (len > AO_CMAC_MAX_LEN)
+               return AO_RADIO_CMAC_LEN_ERROR;
+       ao_mutex_get(&ao_radio_cmac_mutex);
+#if AO_LED_RX
+       ao_led_on(AO_LED_RX);
+#endif
+       i = radio_cmac_recv(len, timeout);
+#if AO_LED_RX
+       ao_led_off(AO_LED_RX);
+#endif
+       if (i == AO_RADIO_CMAC_OK)
+               ao_xmemcpy(packet, cmac_data, len);
+       ao_mutex_put(&ao_radio_cmac_mutex);
+       return i;
+}
+
diff --git a/src/core/ao_radio_cmac.h b/src/core/ao_radio_cmac.h
new file mode 100644 (file)
index 0000000..e86f31e
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_RADIO_CMAC_H_
+#define _AO_RADIO_CMAC_H_
+
+#include <ao_aes.h>
+
+#define AO_CMAC_KEY_LEN                AO_AES_LEN
+#define AO_CMAC_MAX_LEN                (128 - AO_CMAC_KEY_LEN)
+
+extern __pdata int8_t ao_radio_cmac_rssi;
+
+int8_t
+ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant;
+
+#define AO_RADIO_CMAC_OK       0
+#define AO_RADIO_CMAC_LEN_ERROR        -1
+#define AO_RADIO_CMAC_CRC_ERROR        -2
+#define AO_RADIO_CMAC_MAC_ERROR        -3
+#define AO_RADIO_CMAC_TIMEOUT  -4
+
+int8_t
+ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant;
+
+void
+ao_radio_cmac_init(void);
+
+#endif /* _AO_RADIO_CMAC_H_ */
diff --git a/src/core/ao_radio_cmac_cmd.c b/src/core/ao_radio_cmac_cmd.c
new file mode 100644 (file)
index 0000000..6441092
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_radio_cmac_cmd.h>
+#include <ao_radio_cmac.h>
+
+static __xdata uint8_t cmac_data[AO_CMAC_MAX_LEN];
+
+static uint8_t
+getnibble(void)
+{
+       int8_t  b;
+
+       b = ao_cmd_hexchar(getchar());
+       if (b < 0) {
+               ao_cmd_status = ao_cmd_lex_error;
+               return 0;
+       }
+       return (uint8_t) b;
+}
+
+static uint8_t
+getbyte(void)
+{
+       uint8_t b;
+       b = getnibble() << 4;
+       b |= getnibble();
+       return b;
+}
+       
+static void
+radio_cmac_send_cmd(void) __reentrant
+{
+       uint8_t i;
+       uint8_t len;
+
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       len = ao_cmd_lex_i;
+       if (len > AO_CMAC_MAX_LEN) {
+               ao_cmd_status = ao_cmd_syntax_error;
+               return;
+       }
+       flush();
+       len = ao_cmd_lex_i;
+       for (i = 0; i < len; i++) {
+               cmac_data[i] = getbyte();
+               if (ao_cmd_status != ao_cmd_success)
+                       return;
+       }
+       ao_radio_cmac_send(cmac_data, len);
+}
+
+static void
+radio_cmac_recv_cmd(void) __reentrant
+{
+       uint8_t         len, i;
+       uint16_t        timeout;
+
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       len = ao_cmd_lex_i;
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       timeout = AO_MS_TO_TICKS(ao_cmd_lex_i);
+       i = ao_radio_cmac_recv(cmac_data, len, timeout);
+       if (i == AO_RADIO_CMAC_OK) {
+               printf ("PACKET ");
+               for (i = 0; i < len; i++)
+                       printf("%02x", cmac_data[i]);
+               printf (" %d\n", ao_radio_cmac_rssi);
+       } else
+               printf ("ERROR %d %d\n", i, ao_radio_cmac_rssi);
+}
+
+static __code struct ao_cmds ao_radio_cmac_cmds[] = {
+       { radio_cmac_send_cmd,  "s <length>\0Send AES-CMAC packet. Bytes to send follow on next line" },
+       { radio_cmac_recv_cmd,  "S <length> <timeout>\0Receive AES-CMAC packet. Timeout in ms" },
+       { 0, NULL },
+};
+
+void
+ao_radio_cmac_cmd_init(void)
+{
+       ao_cmd_register(&ao_radio_cmac_cmds[0]);
+}
diff --git a/src/core/ao_radio_cmac_cmd.h b/src/core/ao_radio_cmac_cmd.h
new file mode 100644 (file)
index 0000000..6b8782d
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_RADIO_CMAC_CMD_H_
+#define _AO_RADIO_CMAC_CMD_H_
+
+void
+ao_radio_cmac_cmd_init(void);
+
+#endif /* _AO_RADIO_CMAC_CMD_H_ */
diff --git a/src/core/ao_report.c b/src/core/ao_report.c
new file mode 100644 (file)
index 0000000..1104cd8
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_flight.h>
+#include <ao_sample.h>
+
+#define BIT(i,x)          ((x) ? (1 << (i)) : 0)
+#define MORSE1(a)          (1 | BIT(3,a))
+#define MORSE2(a,b)        (2 | BIT(3,a) | BIT(4,b))
+#define MORSE3(a,b,c)      (3 | BIT(3,a) | BIT(4,b) | BIT(5,c))
+#define MORSE4(a,b,c,d)    (4 | BIT(3,a) | BIT(4,b) | BIT(5,c) | BIT(6,d))
+#define MORSE5(a,b,c,d,e)  (5 | BIT(3,a) | BIT(4,b) | BIT(5,c) | BIT(6,d) | BIT(7,e))
+
+static const uint8_t flight_reports[] = {
+       MORSE3(0,0,0),          /* startup, 'S' */
+       MORSE2(0,0),            /* idle 'I' */
+       MORSE4(0,1,1,0),        /* pad 'P' */
+       MORSE4(1,0,0,0),        /* boost 'B' */
+       MORSE4(0,0,1,0),        /* fast 'F' */
+       MORSE4(1,0,1,0),        /* coast 'C' */
+       MORSE3(1,0,0),          /* drogue 'D' */
+       MORSE2(1,1),            /* main 'M' */
+       MORSE4(0,1,0,0),        /* landed 'L' */
+       MORSE4(1,0,0,1),        /* invalid 'X' */
+};
+
+#if HAS_BEEP
+#define low(time)      ao_beep_for(AO_BEEP_LOW, time)
+#define mid(time)      ao_beep_for(AO_BEEP_MID, time)
+#define high(time)     ao_beep_for(AO_BEEP_HIGH, time)
+#else
+#define low(time)      ao_led_for(AO_LED_GREEN, time)
+#define mid(time)      ao_led_for(AO_LED_RED, time)
+#define high(time)     ao_led_for(AO_LED_GREEN|AO_LED_RED, time)
+#endif
+#define pause(time)    ao_delay(time)
+
+static __pdata enum ao_flight_state ao_report_state;
+
+static void
+ao_report_beep(void) __reentrant
+{
+       uint8_t r = flight_reports[ao_flight_state];
+       uint8_t l = r & 7;
+
+       if (!r)
+               return;
+       while (l--) {
+               if (r & 8)
+                       mid(AO_MS_TO_TICKS(600));
+               else
+                       mid(AO_MS_TO_TICKS(200));
+               pause(AO_MS_TO_TICKS(200));
+               r >>= 1;
+       }
+       pause(AO_MS_TO_TICKS(400));
+}
+
+static void
+ao_report_digit(uint8_t digit) __reentrant
+{
+       if (!digit) {
+               mid(AO_MS_TO_TICKS(500));
+               pause(AO_MS_TO_TICKS(200));
+       } else {
+               while (digit--) {
+                       mid(AO_MS_TO_TICKS(200));
+                       pause(AO_MS_TO_TICKS(200));
+               }
+       }
+       pause(AO_MS_TO_TICKS(300));
+}
+
+static void
+ao_report_altitude(void)
+{
+       __pdata int16_t agl = ao_max_height;
+       __xdata uint8_t digits[10];
+       __pdata uint8_t ndigits, i;
+
+       if (agl < 0)
+               agl = 0;
+       ndigits = 0;
+       do {
+               digits[ndigits++] = agl % 10;
+               agl /= 10;
+       } while (agl);
+
+       i = ndigits;
+       do
+               ao_report_digit(digits[--i]);
+       while (i != 0);
+}
+
+#if HAS_IGNITE_REPORT
+static uint8_t
+ao_report_igniter_ready(enum ao_igniter igniter)
+{
+       return ao_igniter_status(igniter) == ao_igniter_ready ? 1 : 0;
+}
+
+uint8_t
+ao_report_igniter(void)
+{
+       return (ao_report_igniter_ready(ao_igniter_drogue) |
+                    (ao_report_igniter_ready(ao_igniter_main) << 1));
+}
+
+static void
+ao_report_continuity(void) __reentrant
+{
+       uint8_t c;
+
+#if !HAS_IGNITE
+       if (!ao_igniter_present)
+               return;
+#endif
+       c = ao_report_igniter();
+       if (c) {
+               while (c--) {
+                       high(AO_MS_TO_TICKS(25));
+                       pause(AO_MS_TO_TICKS(100));
+               }
+       } else {
+               c = 10;
+               while (c--) {
+                       high(AO_MS_TO_TICKS(20));
+                       low(AO_MS_TO_TICKS(20));
+               }
+       }
+#if HAS_LOG
+       if (ao_log_full()) {
+               pause(AO_MS_TO_TICKS(100));
+               c = 2;
+               while (c--) {
+                       low(AO_MS_TO_TICKS(100));
+                       mid(AO_MS_TO_TICKS(100));
+                       high(AO_MS_TO_TICKS(100));
+                       mid(AO_MS_TO_TICKS(100));
+               }
+       }
+#endif
+}
+#endif
+
+void
+ao_report(void)
+{
+       ao_report_state = ao_flight_state;
+       for(;;) {
+               ao_report_beep();
+               if (ao_flight_state == ao_flight_landed) {
+                       ao_report_altitude();
+#if HAS_FLIGHT
+                       ao_delay(AO_SEC_TO_TICKS(5));
+                       continue;
+#endif
+               }
+#if HAS_IGNITE_REPORT
+               if (ao_flight_state == ao_flight_idle)
+                       ao_report_continuity();
+               while (ao_flight_state == ao_flight_pad) {
+                       uint8_t c;
+                       ao_report_continuity();
+                       c = 50;
+                       while (c-- && ao_flight_state == ao_flight_pad)
+                               pause(AO_MS_TO_TICKS(100));
+               }
+#endif
+
+               while (ao_report_state == ao_flight_state)
+                       ao_sleep(DATA_TO_XDATA(&ao_flight_state));
+               ao_report_state = ao_flight_state;
+       }
+}
+
+static __xdata struct ao_task ao_report_task;
+
+void
+ao_report_init(void)
+{
+       ao_add_task(&ao_report_task, ao_report, "report");
+}
diff --git a/src/core/ao_rssi.c b/src/core/ao_rssi.c
new file mode 100644 (file)
index 0000000..e3964d2
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static __xdata volatile uint16_t       ao_rssi_time;
+static __pdata volatile uint16_t       ao_rssi_delay;
+static __pdata uint8_t                 ao_rssi_led;
+
+void
+ao_rssi(void)
+{
+       for (;;) {
+               while ((int16_t) (ao_time() - ao_rssi_time) > AO_SEC_TO_TICKS(3))
+                       ao_sleep(&ao_rssi_time);
+               ao_led_for(ao_rssi_led, AO_MS_TO_TICKS(100));
+               ao_delay(ao_rssi_delay);
+       }
+}
+
+void
+ao_rssi_set(int rssi_value)
+{
+       if (rssi_value > 0)
+               rssi_value = 0;
+       ao_rssi_delay = AO_MS_TO_TICKS((-rssi_value) * 5);
+       ao_rssi_time = ao_time();
+       ao_wakeup(&ao_rssi_time);
+}
+
+__xdata struct ao_task ao_rssi_task;
+
+void
+ao_rssi_init(uint8_t rssi_led)
+{
+       ao_rssi_led = rssi_led;
+       ao_rssi_delay = 0;
+       ao_add_task(&ao_rssi_task, ao_rssi, "rssi");
+}
diff --git a/src/core/ao_sample.c b/src/core/ao_sample.c
new file mode 100644 (file)
index 0000000..985c094
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#include <ao_data.h>
+#endif
+
+/*
+ * Current sensor values
+ */
+
+#ifndef PRES_TYPE
+#define PRES_TYPE int32_t
+#define ALT_TYPE int32_t
+#define ACCEL_TYPE int16_t
+#endif
+
+__pdata uint16_t       ao_sample_tick;         /* time of last data */
+__pdata pres_t         ao_sample_pres;
+__pdata alt_t          ao_sample_alt;
+__pdata alt_t          ao_sample_height;
+#if HAS_ACCEL
+__pdata accel_t                ao_sample_accel;
+#endif
+
+__data uint8_t         ao_sample_data;
+
+/*
+ * Sensor calibration values
+ */
+
+__pdata pres_t         ao_ground_pres;         /* startup pressure */
+__pdata alt_t          ao_ground_height;       /* MSL of ao_ground_pres */
+
+#if HAS_ACCEL
+__pdata accel_t                ao_ground_accel;        /* startup acceleration */
+__pdata accel_t                ao_accel_2g;            /* factory accel calibration */
+__pdata int32_t                ao_accel_scale;         /* sensor to m/s² conversion */
+#endif
+
+static __pdata uint8_t ao_preflight;           /* in preflight mode */
+
+static __pdata uint16_t        nsamples;
+__pdata int32_t ao_sample_pres_sum;
+#if HAS_ACCEL
+__pdata int32_t ao_sample_accel_sum;
+#endif
+
+static void
+ao_sample_preflight_add(void)
+{
+#if HAS_ACCEL
+       ao_sample_accel_sum += ao_sample_accel;
+#endif
+       ao_sample_pres_sum += ao_sample_pres;
+       ++nsamples;
+}
+
+static void
+ao_sample_preflight_set(void)
+{
+#if HAS_ACCEL
+       ao_ground_accel = ao_sample_accel_sum >> 9;
+       ao_sample_accel_sum = 0;
+#endif
+       ao_ground_pres = ao_sample_pres_sum >> 9;
+       ao_ground_height = pres_to_altitude(ao_ground_pres);
+       nsamples = 0;
+       ao_sample_pres_sum = 0;
+}
+
+static void
+ao_sample_preflight(void)
+{
+       /* startup state:
+        *
+        * Collect 512 samples of acceleration and pressure
+        * data and average them to find the resting values
+        */
+       if (nsamples < 512) {
+               ao_sample_preflight_add();
+       } else {
+#if HAS_ACCEL
+               ao_accel_2g = ao_config.accel_minus_g - ao_config.accel_plus_g;
+               ao_accel_scale = to_fix32(GRAVITY * 2 * 16) / ao_accel_2g;
+#endif
+               ao_sample_preflight_set();
+               ao_preflight = FALSE;
+       }
+}
+
+/*
+ * While in pad mode, constantly update the ground state by
+ * re-averaging the data.  This tracks changes in orientation, which
+ * might be caused by adjustments to the rocket on the pad and
+ * pressure, which might be caused by changes in the weather.
+ */
+
+static void
+ao_sample_preflight_update(void)
+{
+       if (nsamples < 512)
+               ao_sample_preflight_add();
+       else if (nsamples < 1024)
+               ++nsamples;
+       else
+               ao_sample_preflight_set();
+}
+
+uint8_t
+ao_sample(void)
+{
+       ao_wakeup(DATA_TO_XDATA(&ao_sample_data));
+       ao_sleep((void *) DATA_TO_XDATA(&ao_data_head));
+       while (ao_sample_data != ao_data_head) {
+               __xdata struct ao_data *ao_data;
+
+               /* Capture a sample */
+               ao_data = (struct ao_data *) &ao_data_ring[ao_sample_data];
+               ao_sample_tick = ao_data->tick;
+
+#if HAS_BARO
+               ao_data_pres_cook(ao_data);
+               ao_sample_pres = ao_data_pres(ao_data);
+               ao_sample_alt = pres_to_altitude(ao_sample_pres);
+               ao_sample_height = ao_sample_alt - ao_ground_height;
+#endif
+
+#if HAS_ACCEL
+               ao_sample_accel = ao_data_accel_cook(ao_data);
+               if (ao_config.pad_orientation != AO_PAD_ORIENTATION_ANTENNA_UP)
+                       ao_sample_accel = ao_data_accel_invert(ao_sample_accel);
+               ao_data_set_accel(ao_data, ao_sample_accel);
+#endif
+
+               if (ao_preflight)
+                       ao_sample_preflight();
+               else {
+                       if (ao_flight_state < ao_flight_boost)
+                               ao_sample_preflight_update();
+                       ao_kalman();
+               }
+               ao_sample_data = ao_data_ring_next(ao_sample_data);
+       }
+       return !ao_preflight;
+}
+
+void
+ao_sample_init(void)
+{
+       ao_config_get();
+       nsamples = 0;
+       ao_sample_pres_sum = 0;
+       ao_sample_pres = 0;
+#if HAS_ACCEL
+       ao_sample_accel_sum = 0;
+       ao_sample_accel = 0;
+#endif
+       ao_sample_data = ao_data_head;
+       ao_preflight = TRUE;
+}
diff --git a/src/core/ao_sample.h b/src/core/ao_sample.h
new file mode 100644 (file)
index 0000000..9336bdf
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_SAMPLE_H_
+#define _AO_SAMPLE_H_
+
+#include <ao_data.h>
+
+/*
+ * ao_sample.c
+ */
+
+/*
+ * Barometer calibration
+ *
+ * We directly sample the barometer. The specs say:
+ *
+ * Pressure range: 15-115 kPa
+ * Voltage at 115kPa: 2.82
+ * Output scale: 27mV/kPa
+ *
+ * If we want to detect launch with the barometer, we need
+ * a large enough bump to not be fooled by noise. At typical
+ * launch elevations (0-2000m), a 200Pa pressure change cooresponds
+ * to about a 20m elevation change. This is 5.4mV, or about 3LSB.
+ * As all of our calculations are done in 16 bits, we'll actually see a change
+ * of 16 times this though
+ *
+ * 27 mV/kPa * 32767 / 3300 counts/mV = 268.1 counts/kPa
+ */
+
+/* Accelerometer calibration
+ *
+ * We're sampling the accelerometer through a resistor divider which
+ * consists of 5k and 10k resistors. This multiplies the values by 2/3.
+ * That goes into the cc1111 A/D converter, which is running at 11 bits
+ * of precision with the bits in the MSB of the 16 bit value. Only positive
+ * values are used, so values should range from 0-32752 for 0-3.3V. The
+ * specs say we should see 40mV/g (uncalibrated), multiply by 2/3 for what
+ * the A/D converter sees (26.67 mV/g). We should see 32752/3300 counts/mV,
+ * for a final computation of:
+ *
+ * 26.67 mV/g * 32767/3300 counts/mV = 264.8 counts/g
+ *
+ * Zero g was measured at 16000 (we would expect 16384).
+ * Note that this value is only require to tell if the
+ * rocket is standing upright. Once that is determined,
+ * the value of the accelerometer is averaged for 100 samples
+ * to find the resting accelerometer value, which is used
+ * for all further flight computations
+ */
+
+#define GRAVITY 9.80665
+
+/*
+ * Above this height, the baro sensor doesn't work
+ */
+#if HAS_MS5607
+#define AO_MAX_BARO_HEIGHT     30000
+#else
+#define AO_MAX_BARO_HEIGHT     12000
+#endif
+
+/*
+ * Above this speed, baro measurements are unreliable
+ */
+#define AO_MAX_BARO_SPEED      200
+
+#define ACCEL_NOSE_UP  (ao_accel_2g >> 2)
+
+/*
+ * Speed and acceleration are scaled by 16 to provide a bit more
+ * resolution while still having reasonable range. Note that this
+ * limits speed to 2047m/s (around mach 6) and acceleration to
+ * 2047m/s² (over 200g)
+ */
+
+#define AO_M_TO_HEIGHT(m)      ((int16_t) (m))
+#define AO_MS_TO_SPEED(ms)     ((int16_t) ((ms) * 16))
+#define AO_MSS_TO_ACCEL(mss)   ((int16_t) ((mss) * 16))
+
+extern __pdata uint16_t        ao_sample_tick;         /* time of last data */
+extern __data uint8_t  ao_sample_adc;          /* Ring position of last processed sample */
+extern __data uint8_t  ao_sample_data;         /* Ring position of last processed sample */
+
+#if HAS_BARO
+extern __pdata pres_t  ao_sample_pres;         /* most recent pressure sensor reading */
+extern __pdata alt_t   ao_sample_alt;          /* MSL of ao_sample_pres */
+extern __pdata alt_t   ao_sample_height;       /* AGL of ao_sample_pres */
+extern __pdata pres_t  ao_ground_pres;         /* startup pressure */
+extern __pdata alt_t   ao_ground_height;       /* MSL of ao_ground_pres */
+#endif
+
+#if HAS_ACCEL
+extern __pdata accel_t ao_sample_accel;        /* most recent accel sensor reading */
+extern __pdata accel_t ao_ground_accel;        /* startup acceleration */
+extern __pdata accel_t         ao_accel_2g;            /* factory accel calibration */
+extern __pdata int32_t ao_accel_scale;         /* sensor to m/s² conversion */
+#endif
+
+void ao_sample_init(void);
+
+/* returns FALSE in preflight mode, TRUE in flight mode */
+uint8_t ao_sample(void);
+
+/*
+ * ao_kalman.c
+ */
+
+#define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
+#define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
+#define from_fix(x)    ((x) >> 16)
+
+extern __pdata int16_t                 ao_height;      /* meters */
+extern __pdata int16_t                 ao_speed;       /* m/s * 16 */
+extern __pdata int16_t                 ao_accel;       /* m/s² * 16 */
+extern __pdata int16_t                 ao_max_height;  /* max of ao_height */
+extern __pdata int16_t                 ao_avg_height;  /* running average of height */
+
+extern __pdata int16_t                 ao_error_h;
+extern __pdata int16_t                 ao_error_h_sq_avg;
+
+#if HAS_ACCEL
+extern __pdata int16_t                 ao_error_a;
+#endif
+
+void ao_kalman(void);
+
+#endif /* _AO_SAMPLE_H_ */
diff --git a/src/core/ao_send_packet.c b/src/core/ao_send_packet.c
new file mode 100644 (file)
index 0000000..1a8e74d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#define AO_MAX_SEND    128
+
+static __xdata uint8_t ao_send[AO_MAX_SEND];
+
+static uint8_t
+getnibble(void)
+{
+       char    c;
+
+       c = getchar();
+       if ('0' <= c && c <= '9')
+               return c - '0';
+       if ('a' <= c && c <= 'f')
+               return c - ('a' - 10);
+       if ('A' <= c && c <= 'F')
+               return c - ('A' - 10);
+       ao_cmd_status = ao_cmd_lex_error;
+       return 0;
+}
+
+static void
+ao_send_packet(void)
+{
+       __pdata uint16_t count;
+       uint8_t b;
+       __pdata uint8_t i;
+
+       ao_cmd_hex();
+       count = ao_cmd_lex_i;
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       if (count > AO_MAX_SEND - 2) {
+               ao_cmd_status = ao_cmd_syntax_error;
+               return;
+       }
+       for (i = 0; i < count; i++) {
+               b = getnibble() << 4;
+               b |= getnibble();
+               if (ao_cmd_status != ao_cmd_success)
+                       return;
+               ao_send[i] = b;
+       }
+       ao_radio_send(ao_send, count);
+}
+
+static __code struct ao_cmds ao_send_packet_cmds[] = {
+       { ao_send_packet, "S <len>\0Send packet. Data on next line" },
+       { 0, NULL }
+};
+
+void
+ao_send_packet_init(void)
+{
+       ao_cmd_register(&ao_send_packet_cmds[0]);
+}
diff --git a/src/core/ao_send_packet.h b/src/core/ao_send_packet.h
new file mode 100644 (file)
index 0000000..526f7b5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_SEND_PACKET_H_
+#define _AO_SEND_PACKET_H_
+
+void
+ao_send_packet_init(void);
+
+#endif /* _AO_SEND_PACKET_H_ */
diff --git a/src/core/ao_serial.h b/src/core/ao_serial.h
new file mode 100644 (file)
index 0000000..53aa8a8
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_SERIAL_H_
+#define _AO_SERIAL_H_
+
+#define AO_SERIAL_SPEED_4800   0
+#define AO_SERIAL_SPEED_9600   1
+#define AO_SERIAL_SPEED_19200  2
+#define AO_SERIAL_SPEED_57600  3
+
+#if HAS_SERIAL_0
+extern volatile __xdata struct ao_fifo ao_serial0_rx_fifo;
+extern volatile __xdata struct ao_fifo ao_serial0_tx_fifo;
+
+char
+ao_serial0_getchar(void);
+
+void
+ao_serial0_putchar(char c);
+
+void
+ao_serial0_drain(void);
+
+void
+ao_serial0_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_1
+extern volatile __xdata struct ao_fifo ao_serial1_rx_fifo;
+extern volatile __xdata struct ao_fifo ao_serial1_tx_fifo;
+
+char
+ao_serial1_getchar(void);
+
+char
+ao_serial1_pollchar(void);
+
+void
+ao_serial1_putchar(char c);
+
+void
+ao_serial1_drain(void);
+
+void
+ao_serial1_set_speed(uint8_t speed);
+#endif
+
+#if HAS_SERIAL_2
+extern volatile __xdata struct ao_fifo ao_serial2_rx_fifo;
+extern volatile __xdata struct ao_fifo ao_serial2_tx_fifo;
+
+char
+ao_serial2_getchar(void);
+
+char
+ao_serial2_pollchar(void);
+
+void
+ao_serial2_putchar(char c);
+
+void
+ao_serial2_drain(void);
+
+void
+ao_serial2_set_speed(uint8_t speed);
+#endif
+
+void
+ao_serial_init(void);
+
+#endif /* _AO_SERIAL_H_ */
diff --git a/src/core/ao_sqrt.c b/src/core/ao_sqrt.c
new file mode 100644 (file)
index 0000000..09c2e31
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/* Adapted from int_sqrt.c in the linux kernel, which is licensed GPLv2 */
+/**
+ * int_sqrt - rough approximation to sqrt
+ * @x: integer of which to calculate the sqrt
+ *
+ * A very rough approximation to the sqrt() function.
+ */
+
+uint32_t
+ao_sqrt(uint32_t op)
+{
+       uint32_t        res = 0;
+       uint32_t        one = 1UL << (sizeof (one) * 8 - 2);
+
+       while (one > op)
+               one >>= 2;
+
+       while (one != 0) {
+               if (op >= res + one) {
+                       op = op - (res + one);
+                       res = res +  2 * one;
+               }
+               res /= 2;
+               one /= 4;
+       }
+       return res;
+}
diff --git a/src/core/ao_state.c b/src/core/ao_state.c
new file mode 100644 (file)
index 0000000..ed197aa
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+const char const * const ao_state_names[] = {
+       "startup", "idle", "pad", "boost", "fast",
+       "coast", "drogue", "main", "landed", "invalid"
+};
diff --git a/src/core/ao_stdio.c b/src/core/ao_stdio.c
new file mode 100644 (file)
index 0000000..656b23c
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+/*
+ * Basic I/O functions to support SDCC stdio package
+ */
+
+#ifndef USE_SERIAL_0_STDIN
+#define USE_SERIAL_0_STDIN     0
+#endif
+#ifndef USE_SERIAL_1_STDIN
+#define USE_SERIAL_1_STDIN     0
+#endif
+#ifndef USE_SERIAL_2_STDIN
+#define USE_SERIAL_2_STDIN     0
+#endif
+#ifndef USE_SERIAL_3_STDIN
+#define USE_SERIAL_3_STDIN     0
+#endif
+#ifndef USE_SERIAL_4_STDIN
+#define USE_SERIAL_4_STDIN     0
+#endif
+#ifndef USE_SERIAL_5_STDIN
+#define USE_SERIAL_5_STDIN     0
+#endif
+#ifndef USE_SERIAL_6_STDIN
+#define USE_SERIAL_6_STDIN     0
+#endif
+#ifndef USE_SERIAL_7_STDIN
+#define USE_SERIAL_7_STDIN     0
+#endif
+#ifndef USE_SERIAL_8_STDIN
+#define USE_SERIAL_8_STDIN     0
+#endif
+#ifndef USE_SERIAL_9_STDIN
+#define USE_SERIAL_9_STDIN     0
+#endif
+
+#define USE_SERIAL_STDIN (USE_SERIAL_0_STDIN + \
+                         USE_SERIAL_1_STDIN +  \
+                         USE_SERIAL_2_STDIN +  \
+                         USE_SERIAL_3_STDIN +  \
+                         USE_SERIAL_4_STDIN +  \
+                         USE_SERIAL_5_STDIN +  \
+                         USE_SERIAL_6_STDIN +  \
+                         USE_SERIAL_7_STDIN +  \
+                         USE_SERIAL_8_STDIN +  \
+                         USE_SERIAL_9_STDIN)
+
+#define AO_NUM_STDIOS  (HAS_USB + PACKET_HAS_SLAVE + USE_SERIAL_STDIN)
+
+__xdata struct ao_stdio ao_stdios[AO_NUM_STDIOS];
+__pdata int8_t ao_cur_stdio;
+__pdata int8_t ao_num_stdios;
+
+void
+putchar(char c)
+{
+#if LOW_LEVEL_DEBUG
+       if (!ao_cur_task) {
+               extern void ao_debug_out(char c);
+               if (c == '\n')
+                       ao_debug_out('\r');
+               ao_debug_out(c);
+               return;
+       }
+#endif
+       if (c == '\n')
+               (*ao_stdios[ao_cur_stdio].putchar)('\r');
+       (*ao_stdios[ao_cur_stdio].putchar)(c);
+}
+
+void
+flush(void)
+{
+       if (ao_stdios[ao_cur_stdio].flush)
+               ao_stdios[ao_cur_stdio].flush();
+}
+
+__xdata uint8_t ao_stdin_ready;
+
+char
+getchar(void) __reentrant __critical
+{
+       char c;
+       int8_t stdio = ao_cur_stdio;
+
+       for (;;) {
+               c = ao_stdios[stdio].pollchar();
+               if (c != AO_READ_AGAIN)
+                       break;
+               if (++stdio == ao_num_stdios)
+                       stdio = 0;
+               if (stdio == ao_cur_stdio)
+                       ao_sleep(&ao_stdin_ready);
+       }
+       ao_cur_stdio = stdio;
+       return c;
+}
+
+uint8_t
+ao_echo(void)
+{
+       return ao_stdios[ao_cur_stdio].echo;
+}
+
+int8_t
+ao_add_stdio(char (*pollchar)(void),
+            void (*putchar)(char),
+            void (*flush)(void)) __reentrant
+{
+       if (ao_num_stdios == AO_NUM_STDIOS)
+               ao_panic(AO_PANIC_STDIO);
+       ao_stdios[ao_num_stdios].pollchar = pollchar;
+       ao_stdios[ao_num_stdios].putchar = putchar;
+       ao_stdios[ao_num_stdios].flush = flush;
+       ao_stdios[ao_num_stdios].echo = 1;
+       return ao_num_stdios++;
+}
diff --git a/src/core/ao_storage.c b/src/core/ao_storage.c
new file mode 100644 (file)
index 0000000..adf7e4d
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_storage.h>
+
+uint8_t
+ao_storage_read(ao_pos_t pos, __xdata void *buf, uint16_t len) __reentrant
+{
+       uint16_t this_len;
+       uint16_t this_off;
+
+       ao_storage_setup();
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+       while (len) {
+
+               /* Compute portion of transfer within
+                * a single block
+                */
+               this_off = (uint16_t) pos & (ao_storage_unit - 1);
+               this_len = ao_storage_unit - this_off;
+               if (this_len > len)
+                       this_len = len;
+
+               if (!ao_storage_device_read(pos, buf, this_len))
+                       return 0;
+
+               /* See how much is left */
+               buf += this_len;
+               len -= this_len;
+               pos += this_len;
+       }
+       return 1;
+}
+
+uint8_t
+ao_storage_write(ao_pos_t pos, __xdata void *buf, uint16_t len) __reentrant
+{
+       uint16_t this_len;
+       uint16_t this_off;
+
+       ao_storage_setup();
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+       while (len) {
+
+               /* Compute portion of transfer within
+                * a single block
+                */
+               this_off = (uint16_t) pos & (ao_storage_unit - 1);
+               this_len = ao_storage_unit - this_off;
+               if (this_len > len)
+                       this_len = len;
+
+               if (!ao_storage_device_write(pos, buf, this_len))
+                       return 0;
+
+               /* See how much is left */
+               buf += this_len;
+               len -= this_len;
+               pos += this_len;
+       }
+       return 1;
+}
+
+static __xdata uint8_t storage_data[8];
+
+static void
+ao_storage_dump(void) __reentrant
+{
+       uint8_t i, j;
+
+       ao_cmd_hex();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       for (i = 0; ; i += 8) {
+               if (ao_storage_read(((uint32_t) (ao_cmd_lex_i) << 8) + i,
+                                 storage_data,
+                                 8)) {
+                       ao_cmd_put16((uint16_t) i);
+                       for (j = 0; j < 8; j++) {
+                               putchar(' ');
+                               ao_cmd_put8(storage_data[j]);
+                       }
+                       putchar ('\n');
+               }
+               if (i == 248)
+                       break;
+       }
+}
+
+#if HAS_STORAGE_DEBUG
+
+/* not enough space for this today
+ */
+static void
+ao_storage_store(void) __reentrant
+{
+       uint16_t block;
+       uint8_t i;
+       uint16_t len;
+       static __xdata uint8_t b;
+       uint32_t addr;
+
+       ao_cmd_hex();
+       block = ao_cmd_lex_i;
+       ao_cmd_hex();
+       i = ao_cmd_lex_i;
+       addr = ((uint32_t) block << 8) | i;
+       ao_cmd_hex();
+       len = ao_cmd_lex_i;
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       while (len--) {
+               ao_cmd_hex();
+               if (ao_cmd_status != ao_cmd_success)
+                       return;
+               b = ao_cmd_lex_i;
+               ao_storage_write(addr, &b, 1);
+               addr++;
+       }
+}
+#endif
+
+void
+ao_storage_zap(void) __reentrant
+{
+       ao_cmd_hex();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       ao_storage_erase((uint32_t) ao_cmd_lex_i << 8);
+}
+
+void
+ao_storage_zapall(void) __reentrant
+{
+       uint32_t        pos;
+
+       ao_cmd_white();
+       if (!ao_match_word("DoIt"))
+               return;
+       for (pos = 0; pos < ao_storage_config; pos += ao_storage_block)
+               ao_storage_erase(pos);
+}
+
+void
+ao_storage_info(void) __reentrant
+{
+       ao_storage_setup();
+       printf("Storage size: %ld\n", (long) ao_storage_total);
+       printf("Storage erase unit: %ld\n", (long) ao_storage_block);
+       ao_storage_device_info();
+}
+
+__code struct ao_cmds ao_storage_cmds[] = {
+       { ao_storage_info, "f\0Show storage" },
+       { ao_storage_dump, "e <block>\0Dump flash" },
+#if HAS_STORAGE_DEBUG
+       { ao_storage_store, "w <block> <start> <len> <data> ...\0Write data to flash" },
+#endif
+       { ao_storage_zap, "z <block>\0Erase <block>" },
+       { ao_storage_zapall,"Z <key>\0Erase all. <key> is doit with D&I" },
+       { 0, NULL },
+};
+
+void
+ao_storage_init(void)
+{
+       ao_storage_device_init();
+       ao_cmd_register(&ao_storage_cmds[0]);
+}
diff --git a/src/core/ao_storage.h b/src/core/ao_storage.h
new file mode 100644 (file)
index 0000000..ea94639
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_STORAGE_H_
+#define _AO_STORAGE_H_
+
+/*
+ * Storage interface, provided by one of the eeprom or flash
+ * drivers
+ */
+
+#ifndef ao_storage_pos_t
+#define ao_storage_pos_t uint32_t
+#endif
+
+typedef ao_storage_pos_t ao_pos_t;
+
+/* Total bytes of available storage */
+extern __pdata ao_pos_t        ao_storage_total;
+
+/* Block size - device is erased in these units. At least 256 bytes */
+extern __pdata ao_pos_t        ao_storage_block;
+
+/* Byte offset of config block. Will be ao_storage_block bytes long */
+extern __pdata ao_pos_t        ao_storage_config;
+
+/* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */
+extern __pdata uint16_t ao_storage_unit;
+
+#define AO_STORAGE_ERASE_LOG   (ao_storage_config + AO_CONFIG_MAX_SIZE)
+
+/* Initialize above values. Can only be called once the OS is running */
+void
+ao_storage_setup(void) __reentrant;
+
+/* Write data. Returns 0 on failure, 1 on success */
+uint8_t
+ao_storage_write(ao_pos_t pos, __xdata void *buf, uint16_t len) __reentrant;
+
+/* Read data. Returns 0 on failure, 1 on success */
+uint8_t
+ao_storage_read(ao_pos_t pos, __xdata void *buf, uint16_t len) __reentrant;
+
+/* Erase a block of storage. This always clears ao_storage_block bytes */
+uint8_t
+ao_storage_erase(ao_pos_t pos) __reentrant;
+
+/* Flush any pending writes to stable storage */
+void
+ao_storage_flush(void) __reentrant;
+
+/* Initialize the storage code */
+void
+ao_storage_init(void);
+
+/*
+ * Low-level functions wrapped by ao_storage.c
+ */
+
+/* Read data within a storage unit */
+uint8_t
+ao_storage_device_read(ao_pos_t pos, __xdata void *buf, uint16_t len) __reentrant;
+
+/* Write data within a storage unit */
+uint8_t
+ao_storage_device_write(ao_pos_t pos, __xdata void *buf, uint16_t len) __reentrant;
+
+/* Initialize low-level device bits */
+void
+ao_storage_device_init(void);
+
+/* Print out information about flash chips */
+void
+ao_storage_device_info(void) __reentrant;
+
+#endif /* _AO_STORAGE_H_ */
diff --git a/src/core/ao_task.c b/src/core/ao_task.c
new file mode 100644 (file)
index 0000000..6565473
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+#define AO_NO_TASK_INDEX       0xff
+
+__xdata struct ao_task * __xdata ao_tasks[AO_NUM_TASKS];
+__data uint8_t ao_num_tasks;
+__data uint8_t ao_cur_task_index;
+__xdata struct ao_task *__data ao_cur_task;
+
+#ifdef ao_arch_task_globals
+ao_arch_task_globals
+#endif
+
+#define AO_CHECK_STACK 0
+
+#if AO_CHECK_STACK
+static uint8_t in_yield;
+
+static inline void ao_check_stack(void) {
+       uint8_t q;
+       if (!in_yield && ao_cur_task && &q < &ao_cur_task->stack[0])
+               ao_panic(AO_PANIC_STACK);
+}
+#else
+#define ao_check_stack()
+#endif
+
+void
+ao_add_task(__xdata struct ao_task * task, void (*start)(void), __code char *name) __reentrant
+{
+       uint8_t task_id;
+       uint8_t t;
+       if (ao_num_tasks == AO_NUM_TASKS)
+               ao_panic(AO_PANIC_NO_TASK);
+       for (task_id = 1; task_id != 0; task_id++) {
+               for (t = 0; t < ao_num_tasks; t++)
+                       if (ao_tasks[t]->task_id == task_id)
+                               break;
+               if (t == ao_num_tasks)
+                       break;
+       }
+       ao_tasks[ao_num_tasks++] = task;
+       task->task_id = task_id;
+       task->name = name;
+       task->wchan = NULL;
+       /*
+        * Construct a stack frame so that it will 'return'
+        * to the start of the task
+        */
+       ao_arch_init_stack(task, start);
+}
+
+/* Task switching function. This must not use any stack variables */
+void
+ao_yield(void) ao_arch_naked_define
+{
+       ao_arch_save_regs();
+
+       if (ao_cur_task_index == AO_NO_TASK_INDEX)
+               ao_cur_task_index = ao_num_tasks-1;
+       else
+       {
+               ao_arch_save_stack();
+       }
+
+       ao_arch_isr_stack();
+
+#if AO_CHECK_STACK
+       in_yield = 1;
+#endif
+       /* Find a task to run. If there isn't any runnable task,
+        * this loop will run forever, which is just fine
+        */
+       {
+               __pdata uint8_t ao_last_task_index = ao_cur_task_index;
+               for (;;) {
+                       ++ao_cur_task_index;
+                       if (ao_cur_task_index == ao_num_tasks)
+                               ao_cur_task_index = 0;
+
+                       ao_cur_task = ao_tasks[ao_cur_task_index];
+
+                       /* Check for ready task */
+                       if (ao_cur_task->wchan == NULL)
+                               break;
+
+                       /* Check if the alarm is set for a time which has passed */
+                       if (ao_cur_task->alarm &&
+                           (int16_t) (ao_time() - ao_cur_task->alarm) >= 0)
+                               break;
+
+                       /* Enter lower power mode when there isn't anything to do */
+                       if (ao_cur_task_index == ao_last_task_index)
+                               ao_arch_cpu_idle();
+               }
+       }
+#if AO_CHECK_STACK
+       cli();
+       in_yield = 0;
+#endif
+       ao_arch_restore_stack();
+}
+
+uint8_t
+ao_sleep(__xdata void *wchan)
+{
+       ao_cur_task->wchan = wchan;
+       ao_yield();
+       if (ao_cur_task->wchan) {
+               ao_cur_task->wchan = NULL;
+               ao_cur_task->alarm = 0;
+               return 1;
+       }
+       return 0;
+}
+
+void
+ao_wakeup(__xdata void *wchan)
+{
+       uint8_t i;
+
+       ao_check_stack();
+       for (i = 0; i < ao_num_tasks; i++)
+               if (ao_tasks[i]->wchan == wchan)
+                       ao_tasks[i]->wchan = NULL;
+}
+
+void
+ao_alarm(uint16_t delay)
+{
+       /* Make sure we sleep *at least* delay ticks, which means adding
+        * one to account for the fact that we may be close to the next tick
+        */
+       if (!(ao_cur_task->alarm = ao_time() + delay + 1))
+               ao_cur_task->alarm = 1;
+}
+
+void
+ao_clear_alarm(void)
+{
+       ao_cur_task->alarm = 0;
+}
+
+static __xdata uint8_t ao_forever;
+
+void
+ao_delay(uint16_t ticks)
+{
+       ao_alarm(ticks);
+       ao_sleep(&ao_forever);
+       ao_clear_alarm();
+}
+
+void
+ao_exit(void)
+{
+       ao_arch_critical(
+               uint8_t i;
+               ao_num_tasks--;
+               for (i = ao_cur_task_index; i < ao_num_tasks; i++)
+                       ao_tasks[i] = ao_tasks[i+1];
+               ao_cur_task_index = AO_NO_TASK_INDEX;
+               ao_yield();
+               );
+       /* we'll never get back here */
+}
+
+void
+ao_task_info(void)
+{
+       uint8_t         i;
+       __xdata struct ao_task *task;
+
+       for (i = 0; i < ao_num_tasks; i++) {
+               task = ao_tasks[i];
+               printf("%12s: wchan %04x\n",
+                      task->name,
+                      (int) task->wchan);
+       }
+}
+
+void
+ao_start_scheduler(void)
+{
+       ao_cur_task_index = AO_NO_TASK_INDEX;
+       ao_cur_task = NULL;
+       ao_yield();
+}
diff --git a/src/core/ao_telem.h b/src/core/ao_telem.h
new file mode 100644 (file)
index 0000000..1a8da29
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_TELEM_H_
+#define _AO_TELEM_H_
+
+#define AO_TELEMETRY_VERSION           4
+
+/*
+ * Telemetry version 4 and higher format:
+ *
+ * General header fields
+ *
+ *     Name            Value
+ *
+ *     VERSION         Telemetry version number (4 or more). Must be first.
+ *     c               Callsign (string, no spaces allowed)
+ *     n               Flight unit serial number (integer)
+ *     f               Flight number (integer)
+ *     r               Packet RSSI value (integer)
+ *     s               Flight computer state (string, no spaces allowed)
+ *     t               Flight computer clock (integer in centiseconds)
+ */
+
+#define AO_TELEM_VERSION       "VERSION"
+#define AO_TELEM_CALL          "c"
+#define AO_TELEM_SERIAL                "n"
+#define AO_TELEM_FLIGHT                "f"
+#define AO_TELEM_RSSI          "r"
+#define AO_TELEM_STATE         "s"
+#define AO_TELEM_TICK          "t"
+
+/*
+ * Raw sensor values
+ *
+ *     Name            Value
+ *     r_a             Accelerometer reading (integer)
+ *     r_b             Barometer reading (integer)
+ *     r_t             Thermometer reading (integer)
+ *     r_v             Battery reading (integer)
+ *     r_d             Drogue continuity (integer)
+ *     r_m             Main continuity (integer)
+ */
+
+#define AO_TELEM_RAW_ACCEL     "r_a"
+#define AO_TELEM_RAW_BARO      "r_b"
+#define AO_TELEM_RAW_THERMO    "r_t"
+#define AO_TELEM_RAW_BATT      "r_v"
+#define AO_TELEM_RAW_DROGUE    "r_d"
+#define AO_TELEM_RAW_MAIN      "r_m"
+
+/*
+ * Sensor calibration values
+ *
+ *     Name            Value
+ *     c_a             Ground accelerometer reading (integer)
+ *     c_b             Ground barometer reading (integer)
+ *     c_p             Accelerometer reading for +1g
+ *     c_m             Accelerometer reading for -1g
+ */
+
+#define AO_TELEM_CAL_ACCEL_GROUND      "c_a"
+#define AO_TELEM_CAL_BARO_GROUND       "c_b"
+#define AO_TELEM_CAL_ACCEL_PLUS                "c_p"
+#define AO_TELEM_CAL_ACCEL_MINUS       "c_m"
+
+/*
+ * Kalman state values
+ *
+ *     Name            Value
+ *     k_h             Height above pad (integer, meters)
+ *     k_s             Vertical speeed (integer, m/s * 16)
+ *     k_a             Vertical acceleration (integer, m/s² * 16)
+ */
+
+#define AO_TELEM_KALMAN_HEIGHT         "k_h"
+#define AO_TELEM_KALMAN_SPEED          "k_s"
+#define AO_TELEM_KALMAN_ACCEL          "k_a"
+
+/*
+ * Ad-hoc flight values
+ *
+ *     Name            Value
+ *     a_a             Acceleration (integer, sensor units)
+ *     a_s             Speed (integer, integrated acceleration value)
+ *     a_b             Barometer reading (integer, sensor units)
+ */
+
+#define AO_TELEM_ADHOC_ACCEL           "a_a"
+#define AO_TELEM_ADHOC_SPEED           "a_s"
+#define AO_TELEM_ADHOC_BARO            "a_b"
+
+/*
+ * GPS values
+ *
+ *     Name            Value
+ *     g               GPS state (string):
+ *                             l       locked
+ *                             u       unlocked
+ *                             e       error (missing or broken)
+ *     g_n             Number of sats used in solution
+ *     g_ns            Latitude (degrees * 10e7)
+ *     g_ew            Longitude (degrees * 10e7)
+ *     g_a             Altitude (integer meters)
+ *     g_Y             GPS year (integer)
+ *     g_M             GPS month (integer - 1-12)
+ *     g_D             GPS day (integer - 1-31)
+ *     g_h             GPS hour (integer - 0-23)
+ *     g_m             GPS minute (integer - 0-59)
+ *     g_s             GPS second (integer - 0-59)
+ *     g_v             GPS vertical speed (integer, cm/sec)
+ *     g_g             GPS horizontal speed (integer, cm/sec)
+ *     g_c             GPS course (integer, 0-359)
+ *     g_hd            GPS hdop (integer * 10)
+ *     g_vd            GPS vdop (integer * 10)
+ *     g_he            GPS h error (integer)
+ *     g_ve            GPS v error (integer)
+ */
+
+#define AO_TELEM_GPS_STATE             "g"
+#define AO_TELEM_GPS_STATE_LOCKED      'l'
+#define AO_TELEM_GPS_STATE_UNLOCKED    'u'
+#define AO_TELEM_GPS_STATE_ERROR       'e'
+#define AO_TELEM_GPS_NUM_SAT           "g_n"
+#define AO_TELEM_GPS_LATITUDE          "g_ns"
+#define AO_TELEM_GPS_LONGITUDE         "g_ew"
+#define AO_TELEM_GPS_ALTITUDE          "g_a"
+#define AO_TELEM_GPS_YEAR              "g_Y"
+#define AO_TELEM_GPS_MONTH             "g_M"
+#define AO_TELEM_GPS_DAY               "g_D"
+#define AO_TELEM_GPS_HOUR              "g_h"
+#define AO_TELEM_GPS_MINUTE            "g_m"
+#define AO_TELEM_GPS_SECOND            "g_s"
+#define AO_TELEM_GPS_VERTICAL_SPEED    "g_v"
+#define AO_TELEM_GPS_HORIZONTAL_SPEED  "g_g"
+#define AO_TELEM_GPS_COURSE            "g_c"
+#define AO_TELEM_GPS_HDOP              "g_hd"
+#define AO_TELEM_GPS_VDOP              "g_vd"
+#define AO_TELEM_GPS_HERROR            "g_he"
+#define AO_TELEM_GPS_VERROR            "g_ve"
+
+/*
+ * GPS satellite values
+ *
+ *     Name            Value
+ *     s_n             Number of satellites reported (integer)
+ *     s_v0            Space vehicle ID (integer) for report 0
+ *     s_c0            C/N0 number (integer) for report 0
+ *     s_v1            Space vehicle ID (integer) for report 1
+ *     s_c1            C/N0 number (integer) for report 1
+ *     ...
+ */
+
+#define AO_TELEM_SAT_NUM               "s_n"
+#define AO_TELEM_SAT_SVID              "s_v"
+#define AO_TELEM_SAT_C_N_0             "s_c"
+
+#endif /* _AO_TELEM_H_ */
diff --git a/src/core/ao_telemetry.c b/src/core/ao_telemetry.c
new file mode 100644 (file)
index 0000000..52ac948
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_product.h"
+
+static __pdata uint16_t ao_telemetry_interval;
+static __pdata uint8_t ao_rdf = 0;
+static __pdata uint16_t ao_rdf_time;
+
+#if defined(MEGAMETRUM)
+#define AO_SEND_MEGA   1
+#endif
+
+#if defined(TELEMETRUM_V_0_1) || defined(TELEMETRUM_V_0_2) || defined(TELEMETRUM_V_1_0) || defined(TELEMETRUM_V_1_1) || defined(TELEBALLOON_V_1_1) || defined(TELEMETRUM_V_1_2)
+#define AO_TELEMETRY_SENSOR    AO_TELEMETRY_SENSOR_TELEMETRUM
+#endif
+
+#if defined(TELEMINI_V_1_0)
+#define AO_TELEMETRY_SENSOR    AO_TELEMETRY_SENSOR_TELEMINI
+#endif
+
+#if defined(TELENANO_V_0_1)
+#define AO_TELEMETRY_SENSOR    AO_TELEMETRY_SENSOR_TELENANO
+#endif
+
+static __xdata union ao_telemetry_all  telemetry;
+
+#if defined AO_TELEMETRY_SENSOR
+/* Send sensor packet */
+static void
+ao_send_sensor(void)
+{
+       __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+                       
+       telemetry.generic.tick = packet->tick;
+       telemetry.generic.type = AO_TELEMETRY_SENSOR;
+
+       telemetry.sensor.state = ao_flight_state;
+#if HAS_ACCEL
+       telemetry.sensor.accel = packet->adc.accel;
+#else
+       telemetry.sensor.accel = 0;
+#endif
+       telemetry.sensor.pres = ao_data_pres(packet);
+       telemetry.sensor.temp = packet->adc.temp;
+       telemetry.sensor.v_batt = packet->adc.v_batt;
+#if HAS_IGNITE
+       telemetry.sensor.sense_d = packet->adc.sense_d;
+       telemetry.sensor.sense_m = packet->adc.sense_m;
+#else
+       telemetry.sensor.sense_d = 0;
+       telemetry.sensor.sense_m = 0;
+#endif
+
+       telemetry.sensor.acceleration = ao_accel;
+       telemetry.sensor.speed = ao_speed;
+       telemetry.sensor.height = ao_height;
+
+       telemetry.sensor.ground_pres = ao_ground_pres;
+#if HAS_ACCEL
+       telemetry.sensor.ground_accel = ao_ground_accel;
+       telemetry.sensor.accel_plus_g = ao_config.accel_plus_g;
+       telemetry.sensor.accel_minus_g = ao_config.accel_minus_g;
+#else
+       telemetry.sensor.ground_accel = 0;
+       telemetry.sensor.accel_plus_g = 0;
+       telemetry.sensor.accel_minus_g = 0;
+#endif
+
+       ao_radio_send(&telemetry, sizeof (telemetry));
+}
+#endif
+
+
+#ifdef AO_SEND_MEGA
+/* Send mega sensor packet */
+static void
+ao_send_mega_sensor(void)
+{
+       __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+                       
+       telemetry.generic.tick = packet->tick;
+       telemetry.generic.type = AO_TELEMETRY_MEGA_SENSOR;
+
+       telemetry.mega_sensor.accel = ao_data_accel(packet);
+       telemetry.mega_sensor.pres = ao_data_pres(packet);
+       telemetry.mega_sensor.temp = ao_data_temp(packet);
+
+#if HAS_MPU6000
+       telemetry.mega_sensor.accel_x = packet->mpu6000.accel_x;
+       telemetry.mega_sensor.accel_y = packet->mpu6000.accel_y;
+       telemetry.mega_sensor.accel_z = packet->mpu6000.accel_z;
+
+       telemetry.mega_sensor.gyro_x = packet->mpu6000.gyro_x;
+       telemetry.mega_sensor.gyro_y = packet->mpu6000.gyro_y;
+       telemetry.mega_sensor.gyro_z = packet->mpu6000.gyro_z;
+#endif
+
+#if HAS_HMC5883
+       telemetry.mega_sensor.mag_x = packet->hmc5883.x;
+       telemetry.mega_sensor.mag_y = packet->hmc5883.y;
+       telemetry.mega_sensor.mag_z = packet->hmc5883.z;
+#endif
+
+       ao_radio_send(&telemetry, sizeof (telemetry));
+}
+
+static __pdata int8_t ao_telemetry_mega_data_max;
+static __pdata int8_t ao_telemetry_mega_data_cur;
+
+/* Send mega data packet */
+static void
+ao_send_mega_data(void)
+{
+       if (--ao_telemetry_mega_data_cur <= 0) {
+               __xdata struct ao_data *packet = (__xdata struct ao_data *) &ao_data_ring[ao_data_ring_prev(ao_sample_data)];
+               uint8_t i;
+
+               telemetry.generic.tick = packet->tick;
+               telemetry.generic.type = AO_TELEMETRY_MEGA_DATA;
+
+               telemetry.mega_data.state = ao_flight_state;
+               telemetry.mega_data.v_batt = packet->adc.v_batt;
+               telemetry.mega_data.v_pyro = packet->adc.v_pbatt;
+
+               /* ADC range is 0-4095, so shift by four to save the high 8 bits */
+               for (i = 0; i < AO_ADC_NUM_SENSE; i++)
+                       telemetry.mega_data.sense[i] = packet->adc.sense[i] >> 4;
+
+               telemetry.mega_data.ground_pres = ao_ground_pres;
+               telemetry.mega_data.ground_accel = ao_ground_accel;
+               telemetry.mega_data.accel_plus_g = ao_config.accel_plus_g;
+               telemetry.mega_data.accel_minus_g = ao_config.accel_minus_g;
+
+               telemetry.mega_data.acceleration = ao_accel;
+               telemetry.mega_data.speed = ao_speed;
+               telemetry.mega_data.height = ao_height;
+
+               ao_radio_send(&telemetry, sizeof (telemetry));
+               ao_telemetry_mega_data_cur = ao_telemetry_mega_data_max;
+       }
+}
+#endif /* AO_SEND_MEGA */
+
+#ifdef AO_SEND_ALL_BARO
+static uint8_t         ao_baro_sample;
+
+static void
+ao_send_baro(void)
+{
+       uint8_t         sample = ao_sample_data;
+       uint8_t         samples = (sample - ao_baro_sample) & (AO_DATA_RING - 1);
+
+       if (samples > 12) {
+               ao_baro_sample = (ao_baro_sample + (samples - 12)) & (AO_DATA_RING - 1);
+               samples = 12;
+       }
+       telemetry.generic.tick = ao_data_ring[sample].tick;
+       telemetry.generic.type = AO_TELEMETRY_BARO;
+       telemetry.baro.samples = samples;
+       for (sample = 0; sample < samples; sample++) {
+               telemetry.baro.baro[sample] = ao_data_ring[ao_baro_sample].adc.pres;
+               ao_baro_sample = ao_data_ring_next(ao_baro_sample);
+       }
+       ao_radio_send(&telemetry, sizeof (telemetry));
+}
+#endif
+
+static __pdata int8_t ao_telemetry_config_max;
+static __pdata int8_t ao_telemetry_config_cur;
+
+static void
+ao_send_configuration(void)
+{
+       if (--ao_telemetry_config_cur <= 0)
+       {
+               telemetry.generic.type = AO_TELEMETRY_CONFIGURATION;
+               telemetry.configuration.device = AO_idProduct_NUMBER;
+               telemetry.configuration.flight = ao_log_full() ? 0 : ao_flight_number;
+               telemetry.configuration.config_major = AO_CONFIG_MAJOR;
+               telemetry.configuration.config_minor = AO_CONFIG_MINOR;
+               telemetry.configuration.apogee_delay = ao_config.apogee_delay;
+               telemetry.configuration.main_deploy = ao_config.main_deploy;
+               telemetry.configuration.flight_log_max = ao_config.flight_log_max >> 10;
+               ao_xmemcpy (telemetry.configuration.callsign,
+                           ao_config.callsign,
+                           AO_MAX_CALLSIGN);
+               ao_xmemcpy (telemetry.configuration.version,
+                           CODE_TO_XDATA(ao_version),
+                           AO_MAX_VERSION);
+               ao_radio_send(&telemetry, sizeof (telemetry));
+               ao_telemetry_config_cur = ao_telemetry_config_max;
+       }
+}
+
+#if HAS_GPS
+
+static __pdata int8_t ao_telemetry_loc_cur;
+static __pdata int8_t ao_telemetry_sat_cur;
+
+static void
+ao_send_location(void)
+{
+       if (--ao_telemetry_loc_cur <= 0)
+       {
+               telemetry.generic.type = AO_TELEMETRY_LOCATION;
+               ao_mutex_get(&ao_gps_mutex);
+               ao_xmemcpy(&telemetry.location.flags,
+                      &ao_gps_data.flags,
+                      26);
+               ao_mutex_put(&ao_gps_mutex);
+               ao_radio_send(&telemetry, sizeof (telemetry));
+               ao_telemetry_loc_cur = ao_telemetry_config_max;
+       }
+}
+
+static void
+ao_send_satellite(void)
+{
+       if (--ao_telemetry_sat_cur <= 0)
+       {
+               telemetry.generic.type = AO_TELEMETRY_SATELLITE;
+               ao_mutex_get(&ao_gps_mutex);
+               telemetry.satellite.channels = ao_gps_tracking_data.channels;
+               ao_xmemcpy(&telemetry.satellite.sats,
+                      &ao_gps_tracking_data.sats,
+                      AO_MAX_GPS_TRACKING * sizeof (struct ao_telemetry_satellite_info));
+               ao_mutex_put(&ao_gps_mutex);
+               ao_radio_send(&telemetry, sizeof (telemetry));
+               ao_telemetry_sat_cur = ao_telemetry_config_max;
+       }
+}
+#endif
+
+#if HAS_COMPANION
+
+static __pdata int8_t ao_telemetry_companion_max;
+static __pdata int8_t ao_telemetry_companion_cur;
+
+static void
+ao_send_companion(void)
+{
+       if (--ao_telemetry_companion_cur <= 0) {
+               telemetry.generic.type = AO_TELEMETRY_COMPANION;
+               telemetry.companion.board_id = ao_companion_setup.board_id;
+               telemetry.companion.update_period = ao_companion_setup.update_period;
+               telemetry.companion.channels = ao_companion_setup.channels;
+               ao_mutex_get(&ao_companion_mutex);
+               ao_xmemcpy(&telemetry.companion.companion_data,
+                      ao_companion_data,
+                      ao_companion_setup.channels * 2);
+               ao_mutex_put(&ao_companion_mutex);
+               ao_radio_send(&telemetry, sizeof (telemetry));
+               ao_telemetry_companion_cur = ao_telemetry_companion_max;
+       }
+}
+#endif
+
+void
+ao_telemetry(void)
+{
+       uint16_t        time;
+       int16_t         delay;
+
+       ao_config_get();
+       if (!ao_config.radio_enable)
+               ao_exit();
+       while (!ao_flight_number)
+               ao_sleep(&ao_flight_number);
+
+       telemetry.generic.serial = ao_serial_number;
+       for (;;) {
+               while (ao_telemetry_interval == 0)
+                       ao_sleep(&telemetry);
+               time = ao_rdf_time = ao_time();
+               while (ao_telemetry_interval) {
+
+
+#ifdef AO_SEND_ALL_BARO
+                       ao_send_baro();
+#endif
+#ifdef AO_SEND_MEGA
+                       ao_send_mega_sensor();
+                       ao_send_mega_data();
+#else
+                       ao_send_sensor();
+#endif
+
+#if HAS_COMPANION
+                       if (ao_companion_running)
+                               ao_send_companion();
+#endif
+                       ao_send_configuration();
+#if HAS_GPS
+                       ao_send_location();
+                       ao_send_satellite();
+#endif
+#ifndef AO_SEND_ALL_BARO
+                       if (ao_rdf &&
+                           (int16_t) (ao_time() - ao_rdf_time) >= 0)
+                       {
+#if HAS_IGNITE_REPORT
+                               uint8_t c;
+#endif
+                               ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
+#if HAS_IGNITE_REPORT
+                               if (ao_flight_state == ao_flight_pad && (c = ao_report_igniter()))
+                                       ao_radio_continuity(c);
+                               else
+#endif
+                                       ao_radio_rdf();
+                       }
+#endif
+                       time += ao_telemetry_interval;
+                       delay = time - ao_time();
+                       if (delay > 0) {
+                               ao_alarm(delay);
+                               ao_sleep(&telemetry);
+                               ao_clear_alarm();
+                       }
+                       else
+                               time = ao_time();
+               bottom: ;
+               }
+       }
+}
+
+void
+ao_telemetry_set_interval(uint16_t interval)
+{
+       int8_t  cur = 0;
+       ao_telemetry_interval = interval;
+       
+#if AO_SEND_MEGA
+       if (interval > 1)
+               ao_telemetry_mega_data_max = 1;
+       else
+               ao_telemetry_mega_data_max = 2;
+       if (ao_telemetry_mega_data_max > cur)
+               cur++;
+       ao_telemetry_mega_data_cur = cur;
+#endif
+
+#if HAS_COMPANION
+       if (!ao_companion_setup.update_period)
+               ao_companion_setup.update_period = AO_SEC_TO_TICKS(1);
+       ao_telemetry_companion_max = ao_companion_setup.update_period / interval;
+       if (ao_telemetry_companion_max > cur)
+               cur++;
+       ao_telemetry_companion_cur = cur;
+#endif
+
+       ao_telemetry_config_max = AO_SEC_TO_TICKS(1) / interval;
+#if HAS_COMPANION
+       if (ao_telemetry_config_max > cur)
+               cur++;
+       ao_telemetry_config_cur = cur;
+#endif
+
+#if HAS_GPS
+       if (ao_telemetry_config_max > cur)
+               cur++;
+       ao_telemetry_loc_cur = cur;
+       if (ao_telemetry_config_max > cur)
+               cur++;
+       ao_telemetry_sat_cur = cur;
+#endif
+       ao_wakeup(&telemetry);
+}
+
+void
+ao_rdf_set(uint8_t rdf)
+{
+       ao_rdf = rdf;
+       if (rdf == 0)
+               ao_radio_rdf_abort();
+       else
+               ao_rdf_time = ao_time() + AO_RDF_INTERVAL_TICKS;
+}
+
+__xdata struct ao_task ao_telemetry_task;
+
+void
+ao_telemetry_init()
+{
+       ao_add_task(&ao_telemetry_task, ao_telemetry, "telemetry");
+}
diff --git a/src/core/ao_telemetry.h b/src/core/ao_telemetry.h
new file mode 100644 (file)
index 0000000..32a1668
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_TELEMETRY_H_
+#define _AO_TELEMETRY_H_
+
+/*
+ * ao_telemetry.c
+ */
+#define AO_MAX_CALLSIGN                        8
+#define AO_MAX_VERSION                 8
+#if LEGACY_MONITOR
+#define AO_MAX_TELEMETRY               128
+#else
+#define AO_MAX_TELEMETRY               32
+#endif
+
+struct ao_telemetry_generic {
+       uint16_t        serial;         /* 0 */
+       uint16_t        tick;           /* 2 */
+       uint8_t         type;           /* 4 */
+       uint8_t         payload[27];    /* 5 */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_SENSOR_TELEMETRUM 0x01
+#define AO_TELEMETRY_SENSOR_TELEMINI   0x02
+#define AO_TELEMETRY_SENSOR_TELENANO   0x03
+
+struct ao_telemetry_sensor {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         state;          /*  5 flight state */
+       int16_t         accel;          /*  6 accelerometer (TM only) */
+       int16_t         pres;           /*  8 pressure sensor */
+       int16_t         temp;           /* 10 temperature sensor */
+       int16_t         v_batt;         /* 12 battery voltage */
+       int16_t         sense_d;        /* 14 drogue continuity sense (TM/Tm) */
+       int16_t         sense_m;        /* 16 main continuity sense (TM/Tm) */
+
+       int16_t         acceleration;   /* 18 m/s² * 16 */
+       int16_t         speed;          /* 20 m/s * 16 */
+       int16_t         height;         /* 22 m */
+
+       int16_t         ground_pres;    /* 24 average pres on pad */
+       int16_t         ground_accel;   /* 26 average accel on pad */
+       int16_t         accel_plus_g;   /* 28 accel calibration at +1g */
+       int16_t         accel_minus_g;  /* 30 accel calibration at -1g */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_CONFIGURATION     0x04
+
+struct ao_telemetry_configuration {
+       uint16_t        serial;                         /*  0 */
+       uint16_t        tick;                           /*  2 */
+       uint8_t         type;                           /*  4 */
+
+       uint8_t         device;                         /*  5 device type */
+       uint16_t        flight;                         /*  6 flight number */
+       uint8_t         config_major;                   /*  8 Config major version */
+       uint8_t         config_minor;                   /*  9 Config minor version */
+       uint16_t        apogee_delay;                   /* 10 Apogee deploy delay in seconds */
+       uint16_t        main_deploy;                    /* 12 Main deploy alt in meters */
+       uint16_t        flight_log_max;                 /* 14 Maximum flight log size in kB */
+       char            callsign[AO_MAX_CALLSIGN];      /* 16 Radio operator identity */
+       char            version[AO_MAX_VERSION];        /* 24 Software version */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_LOCATION          0x05
+
+#define AO_GPS_MODE_NOT_VALID          'N'
+#define AO_GPS_MODE_AUTONOMOUS         'A'
+#define AO_GPS_MODE_DIFFERENTIAL       'D'
+#define AO_GPS_MODE_ESTIMATED          'E'
+#define AO_GPS_MODE_MANUAL             'M'
+#define AO_GPS_MODE_SIMULATED          'S'
+
+struct ao_telemetry_location {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         flags;          /*  5 Number of sats and other flags */
+       int16_t         altitude;       /*  6 GPS reported altitude (m) */
+       int32_t         latitude;       /*  8 latitude (degrees * 10⁷) */
+       int32_t         longitude;      /* 12 longitude (degrees * 10⁷) */
+       uint8_t         year;           /* 16 (- 2000) */
+       uint8_t         month;          /* 17 (1-12) */
+       uint8_t         day;            /* 18 (1-31) */
+       uint8_t         hour;           /* 19 (0-23) */
+       uint8_t         minute;         /* 20 (0-59) */
+       uint8_t         second;         /* 21 (0-59) */
+       uint8_t         pdop;           /* 22 (m * 5) */
+       uint8_t         hdop;           /* 23 (m * 5) */
+       uint8_t         vdop;           /* 24 (m * 5) */
+       uint8_t         mode;           /* 25 */
+       uint16_t        ground_speed;   /* 26 cm/s */
+       int16_t         climb_rate;     /* 28 cm/s */
+       uint8_t         course;         /* 30 degrees / 2 */
+       uint8_t         unused[1];      /* 31 */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_SATELLITE         0x06
+
+struct ao_telemetry_satellite_info {
+       uint8_t         svid;
+       uint8_t         c_n_1;
+};
+
+struct ao_telemetry_satellite {
+       uint16_t                                serial;         /*  0 */
+       uint16_t                                tick;           /*  2 */
+       uint8_t                                 type;           /*  4 */
+       uint8_t                                 channels;       /*  5 number of reported sats */
+
+       struct ao_telemetry_satellite_info      sats[12];       /* 6 */
+       uint8_t                                 unused[2];      /* 30 */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_COMPANION         0x07
+
+#define AO_COMPANION_MAX_CHANNELS      12
+
+struct ao_telemetry_companion {
+       uint16_t                                serial;         /*  0 */
+       uint16_t                                tick;           /*  2 */
+       uint8_t                                 type;           /*  4 */
+       uint8_t                                 board_id;       /*  5 */
+
+       uint8_t                                 update_period;  /*  6 */
+       uint8_t                                 channels;       /*  7 */
+       uint16_t                                companion_data[AO_COMPANION_MAX_CHANNELS];      /*  8 */
+       /* 32 */
+};
+       
+#define AO_TELEMETRY_MEGA_SENSOR       0x08
+
+struct ao_telemetry_mega_sensor {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         pad5;           /*  5 */
+       int16_t         accel;          /*  6 Z axis */
+
+       int32_t         pres;           /*  8 Pa * 10 */
+       int16_t         temp;           /* 12 °C * 100 */
+
+       int16_t         accel_x;        /* 14 */
+       int16_t         accel_y;        /* 16 */
+       int16_t         accel_z;        /* 18 */
+
+       int16_t         gyro_x;         /* 20 */
+       int16_t         gyro_y;         /* 22 */
+       int16_t         gyro_z;         /* 24 */
+
+       int16_t         mag_x;          /* 26 */
+       int16_t         mag_y;          /* 28 */
+       int16_t         mag_z;          /* 30 */
+       /* 32 */
+};
+       
+#define AO_TELEMETRY_MEGA_DATA         0x09
+
+struct ao_telemetry_mega_data {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         state;          /*  5 flight state */
+
+       int16_t         v_batt;         /*  6 battery voltage */
+       int16_t         v_pyro;         /*  8 pyro battery voltage */
+       int8_t          sense[6];       /* 10 continuity sense */
+
+       int32_t         ground_pres;    /* 16 average pres on pad */
+       int16_t         ground_accel;   /* 20 average accel on pad */
+       int16_t         accel_plus_g;   /* 22 accel calibration at +1g */
+       int16_t         accel_minus_g;  /* 24 accel calibration at -1g */
+
+       int16_t         acceleration;   /* 26 m/s² * 16 */
+       int16_t         speed;          /* 28 m/s * 16 */
+       int16_t         height;         /* 30 m */
+       /* 32 */
+};
+
+
+/* #define AO_SEND_ALL_BARO */
+
+#define AO_TELEMETRY_BARO              0x80
+
+/*
+ * This packet allows the full sampling rate baro
+ * data to be captured over the RF link so that the
+ * flight software can be tested using 'real' data.
+ *
+ * Along with this telemetry packet, the flight
+ * code is modified to send full-rate telemetry all the time
+ * and never send an RDF tone; this ensure that the full radio
+ * link is available.
+ */
+struct ao_telemetry_baro {
+       uint16_t                                serial;         /*  0 */
+       uint16_t                                tick;           /*  2 */
+       uint8_t                                 type;           /*  4 */
+       uint8_t                                 samples;        /*  5 number samples */
+
+       int16_t                                 baro[12];       /* 6 samples */
+       /* 32 */
+};
+
+union ao_telemetry_all {
+       struct ao_telemetry_generic             generic;
+       struct ao_telemetry_sensor              sensor;
+       struct ao_telemetry_configuration       configuration;
+       struct ao_telemetry_location            location;
+       struct ao_telemetry_satellite           satellite;
+       struct ao_telemetry_companion           companion;
+       struct ao_telemetry_mega_sensor         mega_sensor;
+       struct ao_telemetry_mega_data           mega_data;
+       struct ao_telemetry_baro                baro;
+};
+
+struct ao_telemetry_all_recv {
+       union ao_telemetry_all          telemetry;
+       int8_t                          rssi;
+       uint8_t                         status;
+};
+
+#endif /* _AO_TELEMETRY_H_ */
diff --git a/src/core/ao_usb.h b/src/core/ao_usb.h
new file mode 100644 (file)
index 0000000..e051db9
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_USB_H_
+#define _AO_USB_H_
+
+/*
+ * ao_usb.c
+ */
+
+/* Put one character to the USB output queue */
+void
+ao_usb_putchar(char c);
+
+/* Get one character from the USB input queue */
+char
+ao_usb_getchar(void);
+
+/* Poll for a charcter on the USB input queue.
+ * returns AO_READ_AGAIN if none are available
+ */
+char
+ao_usb_pollchar(void);
+
+/* Flush the USB output queue */
+void
+ao_usb_flush(void);
+
+/* Enable the USB controller */
+void
+ao_usb_enable(void);
+
+/* Disable the USB controller */
+void
+ao_usb_disable(void);
+
+/* Initialize the USB system */
+void
+ao_usb_init(void);
+
+extern __code __at (0x00aa) uint8_t ao_usb_descriptors [];
+
+#define AO_USB_SETUP_DIR_MASK  (0x01 << 7)
+#define AO_USB_SETUP_TYPE_MASK (0x03 << 5)
+#define AO_USB_SETUP_RECIP_MASK        (0x1f)
+
+#define AO_USB_DIR_OUT                 0
+#define AO_USB_DIR_IN                  (1 << 7)
+
+#define AO_USB_TYPE_STANDARD           0
+#define AO_USB_TYPE_CLASS              (1 << 5)
+#define AO_USB_TYPE_VENDOR             (2 << 5)
+#define AO_USB_TYPE_RESERVED           (3 << 5)
+
+#define AO_USB_RECIP_DEVICE            0
+#define AO_USB_RECIP_INTERFACE         1
+#define AO_USB_RECIP_ENDPOINT          2
+#define AO_USB_RECIP_OTHER             3
+
+/* standard requests */
+#define        AO_USB_REQ_GET_STATUS           0x00
+#define AO_USB_REQ_CLEAR_FEATURE       0x01
+#define AO_USB_REQ_SET_FEATURE         0x03
+#define AO_USB_REQ_SET_ADDRESS         0x05
+#define AO_USB_REQ_GET_DESCRIPTOR      0x06
+#define AO_USB_REQ_SET_DESCRIPTOR      0x07
+#define AO_USB_REQ_GET_CONFIGURATION   0x08
+#define AO_USB_REQ_SET_CONFIGURATION   0x09
+#define AO_USB_REQ_GET_INTERFACE       0x0A
+#define AO_USB_REQ_SET_INTERFACE       0x0B
+#define AO_USB_REQ_SYNCH_FRAME         0x0C
+
+#define AO_USB_DESC_DEVICE             1
+#define AO_USB_DESC_CONFIGURATION      2
+#define AO_USB_DESC_STRING             3
+#define AO_USB_DESC_INTERFACE          4
+#define AO_USB_DESC_ENDPOINT           5
+#define AO_USB_DESC_DEVICE_QUALIFIER   6
+#define AO_USB_DESC_OTHER_SPEED                7
+#define AO_USB_DESC_INTERFACE_POWER    8
+
+#define AO_USB_GET_DESC_TYPE(x)                (((x)>>8)&0xFF)
+#define AO_USB_GET_DESC_INDEX(x)       ((x)&0xFF)
+
+#define AO_USB_CONTROL_EP      0
+#define AO_USB_CONTROL_SIZE    32
+
+#define AO_USB_INT_EP          1
+#define AO_USB_INT_SIZE                8
+
+#define AO_USB_OUT_EP          4
+#define AO_USB_IN_EP           5
+/*
+ * USB bulk packets can only come in 8, 16, 32 and 64
+ * byte sizes, so we'll use 64 for everything
+ */
+#define AO_USB_IN_SIZE         64
+#define AO_USB_OUT_SIZE                64
+
+#define AO_USB_EP0_IDLE                0
+#define AO_USB_EP0_DATA_IN     1
+#define AO_USB_EP0_DATA_OUT    2
+
+#define LE_WORD(x)    ((x)&0xFF),((uint8_t) (((uint16_t) (x))>>8))
+
+/* CDC definitions */
+#define AO_USB_CS_INTERFACE            0x24
+#define AO_USB_CS_ENDPOINT             0x25
+
+#define AO_USB_SET_LINE_CODING         0x20
+#define AO_USB_GET_LINE_CODING         0x21
+#define AO_USB_SET_CONTROL_LINE_STATE  0x22
+
+/* Data structure for GET_LINE_CODING / SET_LINE_CODING class requests */
+struct ao_usb_line_coding {
+       uint32_t        rate;
+       uint8_t         char_format;
+       uint8_t         parity;
+       uint8_t         data_bits;
+} ;
+
+#endif /* _AO_USB_H_ */
diff --git a/src/drivers/ao_25lc1024.c b/src/drivers/ao_25lc1024.c
new file mode 100644 (file)
index 0000000..fac0a43
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_25lc1024.h"
+
+#define EE_BLOCK_SIZE  ((uint16_t) (256))
+#define EE_BLOCK_SHIFT 8
+#define EE_DEVICE_SIZE ((uint32_t) 128 * (uint32_t) 1024)
+
+/* Total bytes of available storage */
+__pdata uint32_t       ao_storage_total;
+
+/* Block size - device is erased in these units. At least 256 bytes */
+__pdata uint32_t       ao_storage_block;
+
+/* Byte offset of config block. Will be ao_storage_block bytes long */
+__pdata uint32_t       ao_storage_config;
+
+/* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */
+__pdata uint16_t       ao_storage_unit;
+
+/*
+ * Using SPI on USART 0, with P1_2 as the chip select
+ */
+
+#define EE_CS_PORT     P1
+#define EE_CS          P1_2
+#define EE_CS_PIN      2
+
+static __xdata uint8_t ao_ee_mutex;
+
+#define ao_ee_delay() do { \
+       _asm nop _endasm; \
+       _asm nop _endasm; \
+       _asm nop _endasm; \
+} while(0)
+
+#define ao_ee_cs_low() ao_spi_get_bit(EE_CS_PORT, EE_CS_PIN, EE_CS, AO_EE_SPI_BUS, AO_SPI_SPEED_FAST)
+
+#define ao_ee_cs_high()        ao_spi_put_bit(EE_CS_PORT, EE_CS_PIN, EE_CS, AO_EE_SPI_BUS)
+
+struct ao_ee_instruction {
+       uint8_t instruction;
+       uint8_t address[3];
+} __xdata ao_ee_instruction;
+
+static void
+ao_ee_write_enable(void)
+{
+       ao_ee_cs_low();
+       ao_ee_instruction.instruction = EE_WREN;
+       ao_spi_send(&ao_ee_instruction, 1, AO_EE_SPI_BUS);
+       ao_ee_cs_high();
+}
+
+static uint8_t
+ao_ee_rdsr(void)
+{
+       ao_ee_cs_low();
+       ao_ee_instruction.instruction = EE_RDSR;
+       ao_spi_send(&ao_ee_instruction, 1, AO_EE_SPI_BUS);
+       ao_spi_recv(&ao_ee_instruction, 1, AO_EE_SPI_BUS);
+       ao_ee_cs_high();
+       return ao_ee_instruction.instruction;
+}
+
+static void
+ao_ee_wrsr(uint8_t status)
+{
+       ao_ee_cs_low();
+       ao_ee_instruction.instruction = EE_WRSR;
+       ao_ee_instruction.address[0] = status;
+       ao_spi_send(&ao_ee_instruction, 2, AO_EE_SPI_BUS);
+       ao_ee_cs_high();
+}
+
+#define EE_BLOCK_NONE  0xffff
+
+static __xdata uint8_t ao_ee_data[EE_BLOCK_SIZE];
+static __pdata uint16_t ao_ee_block = EE_BLOCK_NONE;
+static __pdata uint8_t ao_ee_block_dirty;
+
+/* Write the current block to the EEPROM */
+static void
+ao_ee_write_block(void)
+{
+       uint8_t status;
+
+       status = ao_ee_rdsr();
+       if (status & (EE_STATUS_BP0|EE_STATUS_BP1|EE_STATUS_WPEN)) {
+               status &= ~(EE_STATUS_BP0|EE_STATUS_BP1|EE_STATUS_WPEN);
+               ao_ee_wrsr(status);
+       }
+       ao_ee_write_enable();
+       ao_ee_cs_low();
+       ao_ee_instruction.instruction = EE_WRITE;
+       ao_ee_instruction.address[0] = ao_ee_block >> 8;
+       ao_ee_instruction.address[1] = ao_ee_block;
+       ao_ee_instruction.address[2] = 0;
+       ao_spi_send(&ao_ee_instruction, 4, AO_EE_SPI_BUS);
+       ao_spi_send(ao_ee_data, EE_BLOCK_SIZE, AO_EE_SPI_BUS);
+       ao_ee_cs_high();
+       for (;;) {
+               uint8_t status = ao_ee_rdsr();
+               if ((status & EE_STATUS_WIP) == 0)
+                       break;
+       }
+}
+
+/* Read the current block from the EEPROM */
+static void
+ao_ee_read_block(void)
+{
+       ao_ee_cs_low();
+       ao_ee_instruction.instruction = EE_READ;
+       ao_ee_instruction.address[0] = ao_ee_block >> 8;
+       ao_ee_instruction.address[1] = ao_ee_block;
+       ao_ee_instruction.address[2] = 0;
+       ao_spi_send(&ao_ee_instruction, 4, AO_EE_SPI_BUS);
+       ao_spi_recv(ao_ee_data, EE_BLOCK_SIZE, AO_EE_SPI_BUS);
+       ao_ee_cs_high();
+}
+
+static void
+ao_ee_flush_internal(void)
+{
+       if (ao_ee_block_dirty) {
+               ao_ee_write_block();
+               ao_ee_block_dirty = 0;
+       }
+}
+
+static void
+ao_ee_fill(uint16_t block)
+{
+       if (block != ao_ee_block) {
+               ao_ee_flush_internal();
+               ao_ee_block = block;
+               ao_ee_read_block();
+       }
+}
+
+uint8_t
+ao_storage_device_write(uint32_t pos, __xdata void *buf, uint16_t len) __reentrant
+{
+       uint16_t block = (uint16_t) (pos >> EE_BLOCK_SHIFT);
+
+       /* Transfer the data */
+       ao_mutex_get(&ao_ee_mutex); {
+               if (len != EE_BLOCK_SIZE)
+                       ao_ee_fill(block);
+               else {
+                       ao_ee_flush_internal();
+                       ao_ee_block = block;
+               }
+               ao_xmemcpy(ao_ee_data + (uint16_t) (pos & 0xff), buf, len);
+               ao_ee_block_dirty = 1;
+       } ao_mutex_put(&ao_ee_mutex);
+       return 1;
+}
+
+uint8_t
+ao_storage_device_read(uint32_t pos, __xdata void *buf, uint16_t len) __reentrant
+{
+       uint16_t block = (uint16_t) (pos >> EE_BLOCK_SHIFT);
+
+       /* Transfer the data */
+       ao_mutex_get(&ao_ee_mutex); {
+               ao_ee_fill(block);
+               ao_xmemcpy(buf, ao_ee_data + (uint16_t) (pos & 0xff), len);
+       } ao_mutex_put(&ao_ee_mutex);
+       return 1;
+}
+
+void
+ao_storage_flush(void) __reentrant
+{
+       ao_mutex_get(&ao_ee_mutex); {
+               ao_ee_flush_internal();
+       } ao_mutex_put(&ao_ee_mutex);
+}
+
+uint8_t
+ao_storage_erase(uint32_t pos) __reentrant
+{
+       ao_mutex_get(&ao_ee_mutex); {
+               ao_ee_flush_internal();
+               ao_ee_block = (uint16_t) (pos >> EE_BLOCK_SHIFT);
+               ao_xmemset(ao_ee_data, 0xff, EE_BLOCK_SIZE);
+               ao_ee_block_dirty = 1;
+       } ao_mutex_put(&ao_ee_mutex);
+       return 1;
+}
+
+static void
+ee_store(void) __reentrant
+{
+}
+
+void
+ao_storage_setup(void)
+{
+       if (ao_storage_total == 0) {
+               ao_storage_total = EE_DEVICE_SIZE;
+               ao_storage_block = EE_BLOCK_SIZE;
+               ao_storage_config = EE_DEVICE_SIZE - EE_BLOCK_SIZE;
+               ao_storage_unit = EE_BLOCK_SIZE;
+       }
+}
+
+void
+ao_storage_device_info(void) __reentrant
+{
+}
+
+/*
+ * To initialize the chip, set up the CS line and
+ * the SPI interface
+ */
+void
+ao_storage_device_init(void)
+{
+       /* set up CS */
+       ao_enable_output(EE_CS_PORT, EE_CS_PIN, EE_CS, 1);
+}
diff --git a/src/drivers/ao_25lc1024.h b/src/drivers/ao_25lc1024.h
new file mode 100644 (file)
index 0000000..44e5238
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/* Defines for the 25LC1024 1Mbit SPI Bus Serial EEPROM */
+
+#ifndef _25LC1024_H_
+#define _25LC1024_H_
+
+#define EE_READ                0x03
+#define EE_WRITE       0x02
+#define EE_WREN                0x06
+#define EE_WRDI                0x04
+#define EE_RDSR                0x05
+#define EE_WRSR                0x01
+#define EE_PE          0x42
+#define EE_SE          0xd8
+#define EE_CE          0xc7
+#define EE_RDID                0xab
+#define EE_DPD         0xb9
+
+#define EE_STATUS_WIP  (1 << 0)
+#define EE_STATUS_WEL  (1 << 1)
+#define EE_STATUS_BP0  (1 << 2)
+#define EE_STATUS_BP1  (1 << 3)
+#define EE_STATUS_WPEN (1 << 7)
+
+#endif /* _25LC1024_H_ */
diff --git a/src/drivers/ao_74hc497.c b/src/drivers/ao_74hc497.c
new file mode 100644 (file)
index 0000000..4c13ee7
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+/*
+ * 74HC597 driver.
+ * Reads a single byte from the shift register
+ */
+
+#include <ao.h>
+#include <ao_74hc497.h>
+
+uint8_t
+ao_74hc497_read(void)
+{
+       static __xdata state;
+       ao_spi_get_bit(AO_74HC497_CS_PORT, AO_74HC497_CS_PIN, AO_74HC497_CS, AO_74HC497_SPI_BUS, AO_SPI_SPEED_FAST);
+       ao_spi_recv(&state, 1, AO_74HC497_SPI_BUS);
+       ao_spi_put_bit(AO_74HC497_CS_PORT, AO_74HC497_CS_PIN, AO_74HC497_CS, AO_74HC497_SPI_BUS);
+       return state;
+}
+
+static void
+ao_74hc497_cmd(void)
+{
+       uint8_t v;
+
+       v = ao_74hc497_read();
+       printf ("Switches: 0x%02x\n", v);
+}
+
+static const struct ao_cmds ao_74hc497_cmds[] = {
+       { ao_74hc497_cmd, "L\0Show 74hc497" },
+       { 0, NULL }
+};
+
+void
+ao_74hc497_init(void)
+{
+       ao_enable_output(AO_74HC497_CS_PORT, AO_74HC497_CS_PIN, AO_74HC497_CS, 1);
+       ao_cmd_register(&ao_74hc497_cmds[0]);
+}
diff --git a/src/drivers/ao_74hc497.h b/src/drivers/ao_74hc497.h
new file mode 100644 (file)
index 0000000..6df7bca
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_74HC497_H_
+#define _AO_74HC497_H_
+
+uint8_t
+ao_74hc497_read(void);
+
+void
+ao_74hc497_init(void);
+
+#endif /* _AO_74HC497_H_ */
diff --git a/src/drivers/ao_at45db161d.c b/src/drivers/ao_at45db161d.c
new file mode 100644 (file)
index 0000000..e7e7415
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_at45db161d.h"
+
+/* Total bytes of available storage */
+__pdata uint32_t       ao_storage_total;
+
+/* Block size - device is erased in these units. At least 256 bytes */
+__pdata uint32_t       ao_storage_block;
+
+/* Byte offset of config block. Will be ao_storage_block bytes long */
+__pdata uint32_t       ao_storage_config;
+
+/* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */
+__pdata uint16_t       ao_storage_unit;
+
+#define FLASH_CS               P1_1
+#define FLASH_CS_INDEX         1
+
+#define FLASH_BLOCK_SIZE_MAX   512
+
+__xdata uint8_t ao_flash_mutex;
+
+#define ao_flash_delay() do { \
+       _asm nop _endasm; \
+       _asm nop _endasm; \
+       _asm nop _endasm; \
+} while(0)
+
+#define ao_flash_cs_low()      ao_spi_get_bit(FLASH_CS_PORT, FLASH_CS_PIN, FLASH_CS, AO_FLASH_SPI_BUS, AO_SPI_SPEED_FAST)
+
+#define ao_flash_cs_high()     ao_spi_put_bit(FLASH_CS_PORT, FLASH_CS_PIN, FLASH_CS, AO_FLASH_SPI_BUS)
+
+struct ao_flash_instruction {
+       uint8_t instruction;
+       uint8_t address[3];
+} __xdata ao_flash_instruction;
+
+static void
+ao_flash_set_pagesize_512(void)
+{
+       ao_flash_cs_low();
+       ao_flash_instruction.instruction = FLASH_SET_CONFIG;
+       ao_flash_instruction.address[0] = FLASH_SET_512_BYTE_0;
+       ao_flash_instruction.address[1] = FLASH_SET_512_BYTE_1;
+       ao_flash_instruction.address[2] = FLASH_SET_512_BYTE_2;
+       ao_spi_send(&ao_flash_instruction, 4, AO_FLASH_SPI_BUS);
+       ao_flash_cs_high();
+}
+
+
+static uint8_t
+ao_flash_read_status(void)
+{
+       ao_flash_cs_low();
+       ao_flash_instruction.instruction = FLASH_READ_STATUS;
+       ao_spi_send(&ao_flash_instruction, 1, AO_FLASH_SPI_BUS);
+       ao_spi_recv(&ao_flash_instruction, 1, AO_FLASH_SPI_BUS);
+       ao_flash_cs_high();
+       return ao_flash_instruction.instruction;
+}
+
+#define FLASH_BLOCK_NONE       0xffff
+
+static __xdata uint8_t ao_flash_data[FLASH_BLOCK_SIZE_MAX];
+static __pdata uint16_t ao_flash_block = FLASH_BLOCK_NONE;
+static __pdata uint8_t ao_flash_block_dirty;
+static __pdata uint8_t  ao_flash_write_pending;
+static __pdata uint8_t ao_flash_setup_done;
+static __pdata uint8_t ao_flash_block_shift;
+static __pdata uint16_t        ao_flash_block_size;
+static __pdata uint16_t        ao_flash_block_mask;
+
+void
+ao_storage_setup(void) __reentrant
+{
+       uint8_t status;
+
+       if (ao_flash_setup_done)
+               return;
+
+       ao_mutex_get(&ao_flash_mutex);
+       if (ao_flash_setup_done) {
+               ao_mutex_put(&ao_flash_mutex);
+               return;
+       }
+
+       /* On first use, check to see if the flash chip has
+        * been programmed to use 512 byte pages. If not, do so.
+        * And then, because the flash part must be power cycled
+        * for that change to take effect, panic.
+        */
+       status = ao_flash_read_status();
+
+       if (!(status & FLASH_STATUS_PAGESIZE_512)) {
+               ao_flash_set_pagesize_512();
+               ao_panic(AO_PANIC_FLASH);
+       }
+
+       switch (status & 0x3c) {
+
+       /* AT45DB321D */
+       case 0x34:
+               ao_flash_block_shift = 9;
+               ao_storage_total = ((uint32_t) 4 * (uint32_t) 1024 * (uint32_t) 1024);
+               break;
+
+       /* AT45DB161D */
+       case 0x2c:
+               ao_flash_block_shift = 9;
+               ao_storage_total = ((uint32_t) 2 * (uint32_t) 1024 * (uint32_t) 1024);
+               break;
+
+       /* AT45DB081D */
+       case 0x24:
+               ao_flash_block_shift = 8;
+               ao_storage_total = ((uint32_t) 1024 * (uint32_t) 1024);
+               break;
+
+       /* AT45DB041D */
+       case 0x1c:
+               ao_flash_block_shift = 8;
+               ao_storage_total = ((uint32_t) 512 * (uint32_t) 1024);
+               break;
+
+       /* AT45DB021D */
+       case 0x14:
+               ao_flash_block_shift = 8;
+               ao_storage_total = ((uint32_t) 256 * (uint32_t) 1024);
+               break;
+
+       /* AT45DB011D */
+       case 0x0c:
+               ao_flash_block_shift = 8;
+               ao_storage_total = ((uint32_t) 128 * (uint32_t) 1024);
+               break;
+
+       default:
+               ao_panic(AO_PANIC_FLASH);
+       }
+       ao_flash_block_size = 1 << ao_flash_block_shift;
+       ao_flash_block_mask = ao_flash_block_size - 1;
+
+       ao_storage_block = ao_flash_block_size;
+       ao_storage_config = ao_storage_total - ao_storage_block;
+       ao_storage_unit = ao_flash_block_size;
+
+       ao_flash_setup_done = 1;
+       ao_mutex_put(&ao_flash_mutex);
+}
+
+static void
+ao_flash_wait_write(void)
+{
+       if (ao_flash_write_pending) {
+               for (;;) {
+                       uint8_t status = ao_flash_read_status();
+                       if ((status & FLASH_STATUS_RDY))
+                               break;
+               }
+               ao_flash_write_pending = 0;
+       }
+}
+
+/* Write the current block to the FLASHPROM */
+static void
+ao_flash_write_block(void)
+{
+       ao_flash_wait_write();
+       ao_flash_cs_low();
+       ao_flash_instruction.instruction = FLASH_WRITE;
+
+       /* 13/14 block bits + 9/8 byte bits (always 0) */
+       ao_flash_instruction.address[0] = ao_flash_block >> (16 - ao_flash_block_shift);
+       ao_flash_instruction.address[1] = ao_flash_block << (ao_flash_block_shift - 8);
+       ao_flash_instruction.address[2] = 0;
+       ao_spi_send(&ao_flash_instruction, 4, AO_FLASH_SPI_BUS);
+       ao_spi_send(ao_flash_data, ao_storage_block, AO_FLASH_SPI_BUS);
+       ao_flash_cs_high();
+       ao_flash_write_pending = 1;
+}
+
+/* Read the current block from the FLASHPROM */
+static void
+ao_flash_read_block(void)
+{
+       ao_flash_wait_write();
+       ao_flash_cs_low();
+       ao_flash_instruction.instruction = FLASH_READ;
+
+       /* 13/14 block bits + 9/8 byte bits (always 0) */
+       ao_flash_instruction.address[0] = ao_flash_block >> (16 - ao_flash_block_shift);
+       ao_flash_instruction.address[1] = ao_flash_block << (ao_flash_block_shift - 8);
+       ao_flash_instruction.address[2] = 0;
+       ao_spi_send(&ao_flash_instruction, 4, AO_FLASH_SPI_BUS);
+       ao_spi_recv(ao_flash_data, ao_flash_block_size, AO_FLASH_SPI_BUS);
+       ao_flash_cs_high();
+}
+
+static void
+ao_flash_flush_internal(void)
+{
+       if (ao_flash_block_dirty) {
+               ao_flash_write_block();
+               ao_flash_block_dirty = 0;
+       }
+}
+
+static void
+ao_flash_fill(uint16_t block)
+{
+       if (block != ao_flash_block) {
+               ao_flash_flush_internal();
+               ao_flash_block = block;
+               ao_flash_read_block();
+       }
+}
+
+uint8_t
+ao_storage_device_write(uint32_t pos, __xdata void *buf, uint16_t len) __reentrant
+{
+       uint16_t block = (uint16_t) (pos >> ao_flash_block_shift);
+
+       /* Transfer the data */
+       ao_mutex_get(&ao_flash_mutex); {
+               if (len != ao_flash_block_size)
+                       ao_flash_fill(block);
+               else {
+                       ao_flash_flush_internal();
+                       ao_flash_block = block;
+               }
+               ao_xmemcpy(ao_flash_data + (uint16_t) (pos & ao_flash_block_mask),
+                      buf,
+                      len);
+               ao_flash_block_dirty = 1;
+       } ao_mutex_put(&ao_flash_mutex);
+       return 1;
+}
+
+uint8_t
+ao_storage_device_read(uint32_t pos, __xdata void *buf, uint16_t len) __reentrant
+{
+       uint16_t block = (uint16_t) (pos >> ao_flash_block_shift);
+
+       /* Transfer the data */
+       ao_mutex_get(&ao_flash_mutex); {
+               ao_flash_fill(block);
+               ao_xmemcpy(buf,
+                      ao_flash_data + (uint16_t) (pos & ao_flash_block_mask),
+                      len);
+       } ao_mutex_put(&ao_flash_mutex);
+       return 1;
+}
+
+void
+ao_storage_flush(void) __reentrant
+{
+       ao_mutex_get(&ao_flash_mutex); {
+               ao_flash_flush_internal();
+       } ao_mutex_put(&ao_flash_mutex);
+}
+
+uint8_t
+ao_storage_erase(uint32_t pos) __reentrant
+{
+       ao_mutex_get(&ao_flash_mutex); {
+               ao_flash_flush_internal();
+               ao_flash_block = (uint16_t) (pos >> ao_flash_block_shift);
+               ao_xmemset(ao_flash_data, 0xff, ao_flash_block_size);
+               ao_flash_block_dirty = 1;
+       } ao_mutex_put(&ao_flash_mutex);
+       return 1;
+}
+
+void
+ao_storage_device_info(void) __reentrant
+{
+       uint8_t status;
+
+       ao_storage_setup();
+       ao_mutex_get(&ao_flash_mutex); {
+               status = ao_flash_read_status();
+               printf ("Flash status: 0x%02x\n", status);
+               printf ("Flash block shift: %d\n", ao_flash_block_shift);
+               printf ("Flash block size: %d\n", ao_flash_block_size);
+               printf ("Flash block mask: %d\n", ao_flash_block_mask);
+               printf ("Flash device size: %ld\n", ao_storage_total);
+       } ao_mutex_put(&ao_flash_mutex);
+}
+
+/*
+ * To initialize the chip, set up the CS line and
+ * the SPI interface
+ */
+void
+ao_storage_device_init(void)
+{
+       /* set up CS */
+       FLASH_CS = 1;
+       P1DIR |= (1 << FLASH_CS_INDEX);
+       P1SEL &= ~(1 << FLASH_CS_INDEX);
+}
diff --git a/src/drivers/ao_at45db161d.h b/src/drivers/ao_at45db161d.h
new file mode 100644 (file)
index 0000000..9ee6f1b
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/* Defines for the Atmel AT45DB161D 16Mbit SPI Bus DataFlash® */
+
+#ifndef _AT45DB161D_H_
+#define _AT45DB161D_H_
+
+/*
+ * We reserve the last block on the device for
+ * configuration space. Writes and reads in this
+ * area return errors.
+ */
+
+
+#define FLASH_READ             0x03
+#define FLASH_WRITE            0x82
+#define FLASH_PAGE_ERASE       0x81
+#define FLASH_READ_STATUS      0xd7
+#define FLASH_SET_CONFIG       0x3d
+
+#define FLASH_SET_512_BYTE_0   0x2a
+#define FLASH_SET_512_BYTE_1   0x80
+#define FLASH_SET_512_BYTE_2   0xa6
+
+#define FLASH_STATUS_RDY               (1 << 7)
+#define FLASH_STATUS_COMP              (1 << 6)
+#define FLASH_STATUS_PROTECT           (1 << 1)
+#define FLASH_STATUS_PAGESIZE_512      (1 << 0)
+
+#endif /* _AT45DB161D_H_ */
diff --git a/src/drivers/ao_btm.c b/src/drivers/ao_btm.c
new file mode 100644 (file)
index 0000000..f193ac8
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#ifndef ao_serial_btm_getchar
+#define ao_serial_btm_putchar  ao_serial1_putchar
+#define ao_serial_btm_pollchar ao_serial1_pollchar
+#define ao_serial_btm_set_speed ao_serial1_set_speed
+#define ao_serial_btm_drain    ao_serial1_drain
+#endif
+
+int8_t                 ao_btm_stdio;
+__xdata uint8_t                ao_btm_connected;
+
+#define BT_DEBUG 0
+
+#if BT_DEBUG
+__xdata char           ao_btm_buffer[256];
+int                    ao_btm_ptr;
+char                   ao_btm_dir;
+
+static void
+ao_btm_add_char(char c)
+{
+       if (ao_btm_ptr < sizeof (ao_btm_buffer))
+               ao_btm_buffer[ao_btm_ptr++] = c;
+}
+
+static void
+ao_btm_log_char(char c, char dir)
+{
+       if (dir != ao_btm_dir) {
+               ao_btm_add_char(dir);
+               ao_btm_dir = dir;
+       }
+       ao_btm_add_char(c);
+}
+
+static void
+ao_btm_log_out_char(char c)
+{
+       ao_btm_log_char(c, '>');
+}
+
+static void
+ao_btm_log_in_char(char c)
+{
+       ao_btm_log_char(c, '<');
+}
+
+/*
+ * Dump everything received from the bluetooth device during startup
+ */
+static void
+ao_btm_dump(void)
+{
+       int i;
+       char c;
+
+       for (i = 0; i < ao_btm_ptr; i++) {
+               c = ao_btm_buffer[i];
+               if (c < ' ' && c != '\n')
+                       printf("\\%03o", ((int) c) & 0xff);
+               else
+                       putchar(ao_btm_buffer[i]);
+       }
+       putchar('\n');
+}
+
+static void
+ao_btm_speed(void)
+{
+       ao_cmd_decimal();
+       if (ao_cmd_lex_u32 == 57600)
+               ao_serial_btm_set_speed(AO_SERIAL_SPEED_57600);
+       else if (ao_cmd_lex_u32 == 19200)
+               ao_serial_btm_set_speed(AO_SERIAL_SPEED_19200);
+       else
+               ao_cmd_status = ao_cmd_syntax_error;
+}
+
+__code struct ao_cmds ao_btm_cmds[] = {
+       { ao_btm_dump,          "d\0Dump btm buffer." },
+       { ao_btm_speed,         "s <19200,57600>\0Set btm serial speed." },
+       { 0, NULL },
+};
+
+#define ao_btm_log_init()      ao_cmd_register(&ao_btm_cmds[0])
+
+#else
+#define ao_btm_log_in_char(c)
+#define ao_btm_log_out_char(c)
+#define ao_btm_log_init()
+#endif
+
+#define AO_BTM_MAX_REPLY       16
+__xdata char           ao_btm_reply[AO_BTM_MAX_REPLY];
+
+/*
+ * Read a line of data from the serial port, truncating
+ * it after a few characters.
+ */
+
+uint8_t
+ao_btm_get_line(void)
+{
+       uint8_t ao_btm_reply_len = 0;
+       char c;
+
+       for (;;) {
+
+               while ((c = ao_serial_btm_pollchar()) != AO_READ_AGAIN) {
+                       ao_btm_log_in_char(c);
+                       if (ao_btm_reply_len < sizeof (ao_btm_reply))
+                               ao_btm_reply[ao_btm_reply_len++] = c;
+                       if (c == '\r' || c == '\n')
+                               goto done;
+               }
+               for (c = 0; c < 10; c++) {
+                       ao_delay(AO_MS_TO_TICKS(10));
+                       if (!ao_fifo_empty(ao_serial1_rx_fifo))
+                               break;
+               }
+               if (c == 10)
+                       goto done;
+       }
+done:
+       for (c = ao_btm_reply_len; c < sizeof (ao_btm_reply);)
+               ao_btm_reply[c++] = '\0';
+       return ao_btm_reply_len;
+}
+
+/*
+ * Drain the serial port completely
+ */
+void
+ao_btm_drain()
+{
+       while (ao_btm_get_line())
+               ;
+}
+
+/*
+ * Set the stdio echo for the bluetooth link
+ */
+void
+ao_btm_echo(uint8_t echo)
+{
+       ao_stdios[ao_btm_stdio].echo = echo;
+}
+
+/*
+ * Delay between command charaters; the BT module
+ * can't keep up with 57600 baud
+ */
+
+void
+ao_btm_putchar(char c)
+{
+       ao_btm_log_out_char(c);
+       ao_serial_btm_putchar(c);
+       ao_delay(1);
+}
+
+/*
+ * Wait for the bluetooth device to return
+ * status from the previously executed command
+ */
+uint8_t
+ao_btm_wait_reply(void)
+{
+       for (;;) {
+               ao_btm_get_line();
+               if (!strncmp(ao_btm_reply, "OK", 2))
+                       return 1;
+               if (!strncmp(ao_btm_reply, "ERROR", 5))
+                       return -1;
+               if (ao_btm_reply[0] == '\0')
+                       return 0;
+       }
+}
+
+void
+ao_btm_string(__code char *cmd)
+{
+       char    c;
+
+       while (c = *cmd++)
+               ao_btm_putchar(c);
+}
+
+uint8_t
+ao_btm_cmd(__code char *cmd)
+{
+       ao_btm_drain();
+       ao_btm_string(cmd);
+       return ao_btm_wait_reply();
+}
+
+uint8_t
+ao_btm_set_name(void)
+{
+       char    sn[8];
+       char    *s = sn + 8;
+       char    c;
+       int     n;
+       ao_btm_string("ATN=TeleBT-");
+       *--s = '\0';
+       *--s = '\r';
+       n = ao_serial_number;
+       do {
+               *--s = '0' + n % 10;
+       } while (n /= 10);
+       while ((c = *s++))
+               ao_btm_putchar(c);
+       return ao_btm_wait_reply();
+}
+
+uint8_t
+ao_btm_try_speed(uint8_t speed)
+{
+       ao_serial_btm_set_speed(speed);
+       ao_serial_btm_drain();
+       (void) ao_btm_cmd("\rATE0\rATQ0\r");
+       if (ao_btm_cmd("AT\r") == 1)
+               return 1;
+       return 0;
+}
+
+/*
+ * A thread to initialize the bluetooth device and
+ * hang around to blink the LED when connected
+ */
+void
+ao_btm(void)
+{
+       /*
+        * Wait for the bluetooth device to boot
+        */
+       ao_delay(AO_SEC_TO_TICKS(3));
+
+       /*
+        * The first time we connect, the BTM-180 comes up at 19200 baud.
+        * After that, it will remember and come up at 57600 baud. So, see
+        * if it is already running at 57600 baud, and if that doesn't work
+        * then tell it to switch to 57600 from 19200 baud.
+        */
+       while (!ao_btm_try_speed(AO_SERIAL_SPEED_57600)) {
+               ao_delay(AO_SEC_TO_TICKS(1));
+               if (ao_btm_try_speed(AO_SERIAL_SPEED_19200))
+                       ao_btm_cmd("ATL4\r");
+               ao_delay(AO_SEC_TO_TICKS(1));
+       }
+
+       /* Disable echo */
+       ao_btm_cmd("ATE0\r");
+
+       /* Enable flow control */
+       ao_btm_cmd("ATC1\r");
+
+       /* Set the reported name to something we can find on the host */
+       ao_btm_set_name();
+
+       /* Turn off status reporting */
+       ao_btm_cmd("ATQ1\r");
+
+       ao_btm_stdio = ao_add_stdio(ao_serial_btm_pollchar,
+                                   ao_serial_btm_putchar,
+                                   NULL);
+       ao_btm_echo(0);
+
+       for (;;) {
+               while (!ao_btm_connected)
+                       ao_sleep(&ao_btm_connected);
+               while (ao_btm_connected) {
+                       ao_led_for(AO_LED_GREEN, AO_MS_TO_TICKS(20));
+                       ao_delay(AO_SEC_TO_TICKS(3));
+               }
+       }
+}
+
+__xdata struct ao_task ao_btm_task;
+
+#if BT_LINK_ON_P2
+#define BT_PICTL_ICON  PICTL_P2ICON
+#define BT_PIFG                P2IFG
+#define BT_PDIR                P2DIR
+#define BT_PINP                P2INP
+#define BT_IEN2_PIE    IEN2_P2IE
+#endif
+#if BT_LINK_ON_P1
+#define BT_PICTL_ICON  PICTL_P1ICON
+#define BT_PIFG                P1IFG
+#define BT_PDIR                P1DIR
+#define BT_PINP                P1INP
+#define BT_IEN2_PIE    IEN2_P1IE
+#endif
+
+void
+ao_btm_check_link() __critical
+{
+       /* Check the pin and configure the interrupt detector to wait for the
+        * pin to flip the other way
+        */
+       if (BT_LINK_PIN) {
+               ao_btm_connected = 0;
+               PICTL |= BT_PICTL_ICON;
+       } else {
+               ao_btm_connected = 1;
+               PICTL &= ~BT_PICTL_ICON;
+       }
+}
+
+void
+ao_btm_isr(void)
+#if BT_LINK_ON_P1
+       __interrupt 15
+#endif
+{
+#if BT_LINK_ON_P1
+       P1IF = 0;
+#endif
+       if (BT_PIFG & (1 << BT_LINK_PIN_INDEX)) {
+               ao_btm_check_link();
+               ao_wakeup(&ao_btm_connected);
+       }
+       BT_PIFG = 0;
+}
+
+void
+ao_btm_init (void)
+{
+       ao_serial_init();
+
+       ao_serial_btm_set_speed(AO_SERIAL_SPEED_19200);
+
+#if BT_LINK_ON_P1
+       /*
+        * Configure ser reset line
+        */
+
+       P1_6 = 0;
+       P1DIR |= (1 << 6);
+#endif
+
+       /*
+        * Configure link status line
+        */
+
+       /* Set pin to input */
+       BT_PDIR &= ~(1 << BT_LINK_PIN_INDEX);
+
+       /* Set pin to tri-state */
+       BT_PINP |= (1 << BT_LINK_PIN_INDEX);
+
+       /* Enable interrupts */
+       IEN2 |= BT_IEN2_PIE;
+
+       /* Check current pin state */
+       ao_btm_check_link();
+
+#if BT_LINK_ON_P2
+       /* Eable the pin interrupt */
+       PICTL |= PICTL_P2IEN;
+#endif
+#if BT_LINK_ON_P1
+       /* Enable pin interrupt */
+       P1IEN |= (1 << BT_LINK_PIN_INDEX);
+#endif
+
+       ao_add_task(&ao_btm_task, ao_btm, "bt");
+       ao_btm_log_init();
+}
diff --git a/src/drivers/ao_button.c b/src/drivers/ao_button.c
new file mode 100644 (file)
index 0000000..a507c90
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_button.h>
+#include <ao_exti.h>
+#if AO_EVENT
+#include <ao_event.h>
+#define ao_button_queue(b,v)   ao_event_put_isr(AO_EVENT_BUTTON, b, v)
+#else
+#define ao_button_queue(b,v)
+#endif
+
+static uint8_t         ao_button[AO_BUTTON_COUNT];
+static AO_TICK_TYPE    ao_button_time[AO_BUTTON_COUNT];
+
+#define AO_DEBOUNCE    AO_MS_TO_TICKS(20)
+
+#define port(q)        AO_BUTTON_ ## q ## _PORT
+#define bit(q) AO_BUTTON_ ## q
+#define pin(q) AO_BUTTON_ ## q ## _PIN
+
+static void
+ao_button_do(uint8_t b, uint8_t v)
+{
+       /* Debounce */
+       if ((AO_TICK_SIGNED) (ao_tick_count - ao_button_time[b]) < AO_DEBOUNCE)
+               return;
+
+       /* pins are inverted */
+       v = !v;
+       if (ao_button[b] != v) {
+               ao_button[b] = v;
+               ao_button_time[b] = ao_tick_count;
+               ao_button_queue(b, v);
+               ao_wakeup(&ao_button[b]);
+       }
+}
+
+#define ao_button_update(b)    ao_button_do(b, ao_gpio_get(port(b), bit(b), pin(b)))
+
+static void
+ao_button_isr(void)
+{
+#if AO_BUTTON_COUNT > 0
+       ao_button_update(0);
+#endif
+#if AO_BUTTON_COUNT > 1
+       ao_button_update(1);
+#endif
+#if AO_BUTTON_COUNT > 2
+       ao_button_update(2);
+#endif
+#if AO_BUTTON_COUNT > 3
+       ao_button_update(3);
+#endif
+#if AO_BUTTON_COUNT > 4
+       ao_button_update(4);
+#endif
+}
+
+#define init(b) do {                                                   \
+               ao_enable_port(port(b));                                \
+                                                                       \
+               ao_exti_setup(port(b), bit(b),                          \
+                             AO_BUTTON_MODE|AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_MED, \
+                             ao_button_isr);                   \
+               ao_exti_enable(port(b), bit(b));                        \
+       } while (0)
+
+void
+ao_button_init(void)
+{
+#if AO_BUTTON_COUNT > 0
+       init(0);
+#endif
+#if AO_BUTTON_COUNT > 1
+       init(1);
+#endif
+}
diff --git a/src/drivers/ao_button.h b/src/drivers/ao_button.h
new file mode 100644 (file)
index 0000000..ce349d6
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_BUTTON_H_
+#define _AO_BUTTON_H_
+
+void
+ao_button_init(void);
+
+#endif /* _AO_BUTTON_H_ */
diff --git a/src/drivers/ao_cc1120.c b/src/drivers/ao_cc1120.c
new file mode 100644 (file)
index 0000000..2f9c296
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_cc1120.h>
+#include <ao_exti.h>
+#include <ao_fec.h>
+#include <ao_packet.h>
+
+uint8_t ao_radio_wake;
+uint8_t ao_radio_mutex;
+uint8_t ao_radio_abort;
+uint8_t ao_radio_in_recv;
+
+#define CC1120_DEBUG   AO_FEC_DEBUG
+#define CC1120_TRACE   0
+
+extern const uint32_t  ao_radio_cal;
+
+#define FOSC   32000000
+
+#define ao_radio_select()      ao_spi_get_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS,AO_SPI_SPEED_1MHz)
+#define ao_radio_deselect()    ao_spi_put_mask(AO_CC1120_SPI_CS_PORT,(1 << AO_CC1120_SPI_CS_PIN),AO_CC1120_SPI_BUS)
+#define ao_radio_spi_send(d,l) ao_spi_send((d), (l), AO_CC1120_SPI_BUS)
+#define ao_radio_spi_send_fixed(d,l) ao_spi_send_fixed((d), (l), AO_CC1120_SPI_BUS)
+#define ao_radio_spi_recv(d,l) ao_spi_recv((d), (l), AO_CC1120_SPI_BUS)
+#define ao_radio_duplex(o,i,l) ao_spi_duplex((o), (i), (l), AO_CC1120_SPI_BUS)
+
+static uint8_t
+ao_radio_reg_read(uint16_t addr)
+{
+       uint8_t data[2];
+       uint8_t d;
+
+#if CC1120_TRACE
+       printf("\t\tao_radio_reg_read (%04x): ", addr); flush();
+#endif
+       if (CC1120_IS_EXTENDED(addr)) {
+               data[0] = ((1 << CC1120_READ)  |
+                          (0 << CC1120_BURST) |
+                          CC1120_EXTENDED);
+               data[1] = addr;
+               d = 2;
+       } else {
+               data[0] = ((1 << CC1120_READ)  |
+                          (0 << CC1120_BURST) |
+                          addr);
+               d = 1;
+       }
+       ao_radio_select();
+       ao_radio_spi_send(data, d);
+       ao_radio_spi_recv(data, 1);
+       ao_radio_deselect();
+#if CC1120_TRACE
+       printf (" %02x\n", data[0]);
+#endif
+       return data[0];
+}
+
+static void
+ao_radio_reg_write(uint16_t addr, uint8_t value)
+{
+       uint8_t data[3];
+       uint8_t d;
+
+#if CC1120_TRACE
+       printf("\t\tao_radio_reg_write (%04x): %02x\n", addr, value);
+#endif
+       if (CC1120_IS_EXTENDED(addr)) {
+               data[0] = ((0 << CC1120_READ)  |
+                          (0 << CC1120_BURST) |
+                          CC1120_EXTENDED);
+               data[1] = addr;
+               d = 2;
+       } else {
+               data[0] = ((0 << CC1120_READ)  |
+                          (0 << CC1120_BURST) |
+                          addr);
+               d = 1;
+       }
+       data[d] = value;
+       ao_radio_select();
+       ao_radio_spi_send(data, d+1);
+       ao_radio_deselect();
+}
+
+static void
+ao_radio_burst_read_start (uint16_t addr)
+{
+       uint8_t data[2];
+       uint8_t d;
+
+       if (CC1120_IS_EXTENDED(addr)) {
+               data[0] = ((1 << CC1120_READ)  |
+                          (1 << CC1120_BURST) |
+                          CC1120_EXTENDED);
+               data[1] = addr;
+               d = 2;
+       } else {
+               data[0] = ((1 << CC1120_READ)  |
+                          (1 << CC1120_BURST) |
+                          addr);
+               d = 1;
+       }
+       ao_radio_select();
+       ao_radio_spi_send(data, d);
+}
+
+static void
+ao_radio_burst_read_stop (void)
+{
+       ao_radio_deselect();
+}
+
+
+static uint8_t
+ao_radio_strobe(uint8_t addr)
+{
+       uint8_t in;
+
+#if CC1120_TRACE
+       printf("\t\tao_radio_strobe (%02x): ", addr); flush();
+#endif
+       ao_radio_select();
+       ao_radio_duplex(&addr, &in, 1);
+       ao_radio_deselect();
+#if CC1120_TRACE
+       printf("%02x\n", in); flush();
+#endif
+       return in;
+}
+
+static uint8_t
+ao_radio_fifo_read(uint8_t *data, uint8_t len)
+{
+       uint8_t addr = ((1 << CC1120_READ)  |
+                       (1 << CC1120_BURST) |
+                       CC1120_FIFO);
+       uint8_t status;
+
+       ao_radio_select();
+       ao_radio_duplex(&addr, &status, 1);
+       ao_radio_spi_recv(data, len);
+       ao_radio_deselect();
+       return status;
+}
+
+static uint8_t
+ao_radio_fifo_write_start(void)
+{
+       uint8_t addr = ((0 << CC1120_READ)  |
+                       (1 << CC1120_BURST) |
+                       CC1120_FIFO);
+       uint8_t status;
+
+       ao_radio_select();
+       ao_radio_duplex(&addr, &status, 1);
+       return status;
+}
+
+static inline uint8_t ao_radio_fifo_write_stop(uint8_t status) {
+       ao_radio_deselect();
+       return status;
+}
+
+static uint8_t
+ao_radio_fifo_write(uint8_t *data, uint8_t len)
+{
+       uint8_t status = ao_radio_fifo_write_start();
+       ao_radio_spi_send(data, len);
+       return ao_radio_fifo_write_stop(status);
+}
+
+static uint8_t
+ao_radio_fifo_write_fixed(uint8_t data, uint8_t len)
+{
+       uint8_t status = ao_radio_fifo_write_start();
+       ao_radio_spi_send_fixed(data, len);
+       return ao_radio_fifo_write_stop(status);
+}
+
+static uint8_t
+ao_radio_tx_fifo_space(void)
+{
+       return CC1120_FIFO_SIZE - ao_radio_reg_read(CC1120_NUM_TXBYTES);
+}
+
+static uint8_t
+ao_radio_status(void)
+{
+       return ao_radio_strobe (CC1120_SNOP);
+}
+
+void
+ao_radio_recv_abort(void)
+{
+       ao_radio_abort = 1;
+       ao_wakeup(&ao_radio_wake);
+}
+
+#define ao_radio_rdf_value 0x55
+
+static uint8_t
+ao_radio_marc_status(void)
+{
+       return ao_radio_reg_read(CC1120_MARC_STATUS1);
+}
+
+static void
+ao_radio_tx_isr(void)
+{
+       ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+       ao_radio_wake = 1;
+       ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_start_tx(void)
+{
+       ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_tx_isr);
+       ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+       ao_radio_strobe(CC1120_STX);
+}
+
+static void
+ao_radio_idle(void)
+{
+       for (;;) {
+               uint8_t state = ao_radio_strobe(CC1120_SIDLE);
+               if ((state >> CC1120_STATUS_STATE) == CC1120_STATUS_STATE_IDLE)
+                       break;
+       }
+}
+
+/*
+ * Packet deviation is 20.5kHz
+ *
+ *     fdev = fosc >> 24 * (256 + dev_m) << dev_e
+ *
+ *             32e6Hz / (2 ** 24) * (256 + 80) * (2 ** 5) = 20508Hz
+ */
+
+#define PACKET_DEV_E   5
+#define PACKET_DEV_M   80
+
+/*
+ * For our packet data, set the symbol rate to 38360 Baud
+ *
+ *              (2**20 + DATARATE_M) * 2 ** DATARATE_E
+ *     Rdata = -------------------------------------- * fosc
+ *                          2 ** 39
+ *
+ *
+ *     DATARATE_M = 239914
+ *     DATARATE_E = 9
+ */
+#define PACKET_DRATE_E 9
+#define PACKET_DRATE_M 239914
+
+static const uint16_t packet_setup[] = {
+       CC1120_DEVIATION_M,     PACKET_DEV_M,
+       CC1120_MODCFG_DEV_E,    ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) |
+                                (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) |
+                                (PACKET_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)),
+       CC1120_DRATE2,          ((PACKET_DRATE_E << CC1120_DRATE2_DATARATE_E) |
+                                (((PACKET_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)),
+       CC1120_DRATE1,          ((PACKET_DRATE_M >> 8) & 0xff),
+       CC1120_DRATE0,          ((PACKET_DRATE_M >> 0) & 0xff),
+       CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
+                                (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
+       CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
+                                (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
+                                (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
+                                (0 << CC1120_PKT_CFG1_APPEND_STATUS)),
+       CC1120_PKT_CFG0,        ((0 << CC1120_PKT_CFG0_RESERVED7) |
+                                (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) |
+                                (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
+                                (0 << CC1120_PKT_CFG0_UART_MODE_EN) |
+                                (0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
+};
+
+static const uint16_t packet_tx_setup[] = {
+       CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
+                                (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
+       CC1120_IOCFG2,          CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG,
+};
+
+static const uint16_t packet_rx_setup[] = {
+       CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
+                                (CC1120_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL << CC1120_PKT_CFG2_PKT_FORMAT)),
+       CC1120_IOCFG2,          CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT,
+};
+
+/*
+ * RDF deviation is 5kHz
+ *
+ *     fdev = fosc >> 24 * (256 + dev_m) << dev_e
+ *
+ *             32e6Hz / (2 ** 24) * (256 + 71) * (2 ** 3) = 4989
+ */
+
+#define RDF_DEV_E      3
+#define RDF_DEV_M      71
+#define RDF_PACKET_LEN 50
+
+/*
+ * For our RDF beacon, set the symbol rate to 2kBaud (for a 1kHz tone)
+ *
+ *              (2**20 - DATARATE_M) * 2 ** DATARATE_E
+ *     Rdata = -------------------------------------- * fosc
+ *                          2 ** 39
+ *
+ *     DATARATE_M = 511705
+ *     DATARATE_E = 6
+ *
+ * To make the tone last for 200ms, we need 2000 * .2 = 400 bits or 50 bytes
+ */
+#define RDF_DRATE_E    5
+#define RDF_DRATE_M    25166
+#define RDF_PACKET_LEN 50
+
+static const uint16_t rdf_setup[] = {
+       CC1120_DEVIATION_M,     RDF_DEV_M,
+       CC1120_MODCFG_DEV_E,    ((CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL << CC1120_MODCFG_DEV_E_MODEM_MODE) |
+                                (CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK << CC1120_MODCFG_DEV_E_MOD_FORMAT) |
+                                (RDF_DEV_E << CC1120_MODCFG_DEV_E_DEV_E)),
+       CC1120_DRATE2,          ((RDF_DRATE_E << CC1120_DRATE2_DATARATE_E) |
+                                (((RDF_DRATE_M >> 16) & CC1120_DRATE2_DATARATE_M_19_16_MASK) << CC1120_DRATE2_DATARATE_M_19_16)),
+       CC1120_DRATE1,          ((RDF_DRATE_M >> 8) & 0xff),
+       CC1120_DRATE0,          ((RDF_DRATE_M >> 0) & 0xff),
+       CC1120_PKT_CFG2,        ((CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR << CC1120_PKT_CFG2_CCA_MODE) |
+                                (CC1120_PKT_CFG2_PKT_FORMAT_NORMAL << CC1120_PKT_CFG2_PKT_FORMAT)),
+       CC1120_PKT_CFG1,        ((0 << CC1120_PKT_CFG1_WHITE_DATA) |
+                                (CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE << CC1120_PKT_CFG1_ADDR_CHECK_CFG) |
+                                (CC1120_PKT_CFG1_CRC_CFG_DISABLED << CC1120_PKT_CFG1_CRC_CFG) |
+                                (0 << CC1120_PKT_CFG1_APPEND_STATUS)),
+       CC1120_PKT_CFG0,        ((0 << CC1120_PKT_CFG0_RESERVED7) |
+                                (CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED << CC1120_PKT_CFG0_LENGTH_CONFIG) |
+                                (0 << CC1120_PKT_CFG0_PKG_BIT_LEN) |
+                                (0 << CC1120_PKT_CFG0_UART_MODE_EN) |
+                                (0 << CC1120_PKT_CFG0_UART_SWAP_EN)),
+};
+
+static uint8_t ao_radio_mode;
+
+#define AO_RADIO_MODE_BITS_PACKET      1
+#define AO_RADIO_MODE_BITS_PACKET_TX   2
+#define AO_RADIO_MODE_BITS_TX_BUF      4
+#define AO_RADIO_MODE_BITS_TX_FINISH   8
+#define AO_RADIO_MODE_BITS_PACKET_RX   16
+#define AO_RADIO_MODE_BITS_RDF         32
+
+#define AO_RADIO_MODE_NONE             0
+#define AO_RADIO_MODE_PACKET_TX_BUF    (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_BUF)
+#define AO_RADIO_MODE_PACKET_TX_FINISH (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_TX | AO_RADIO_MODE_BITS_TX_FINISH)
+#define AO_RADIO_MODE_PACKET_RX                (AO_RADIO_MODE_BITS_PACKET | AO_RADIO_MODE_BITS_PACKET_RX)
+#define AO_RADIO_MODE_RDF              (AO_RADIO_MODE_BITS_RDF | AO_RADIO_MODE_BITS_TX_FINISH)
+
+static void
+ao_radio_set_mode(uint8_t new_mode)
+{
+       uint8_t changes;
+       int i;
+
+       if (new_mode == ao_radio_mode)
+               return;
+
+       changes = new_mode & (~ao_radio_mode);
+       if (changes & AO_RADIO_MODE_BITS_PACKET)
+               for (i = 0; i < sizeof (packet_setup) / sizeof (packet_setup[0]); i += 2)
+                       ao_radio_reg_write(packet_setup[i], packet_setup[i+1]);
+
+       if (changes & AO_RADIO_MODE_BITS_PACKET_TX)
+               for (i = 0; i < sizeof (packet_tx_setup) / sizeof (packet_tx_setup[0]); i += 2)
+                       ao_radio_reg_write(packet_tx_setup[i], packet_tx_setup[i+1]);
+               
+       if (changes & AO_RADIO_MODE_BITS_TX_BUF)
+               ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_TXFIFO_THR);
+
+       if (changes & AO_RADIO_MODE_BITS_TX_FINISH)
+               ao_radio_reg_write(CC1120_IOCFG2, CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG);
+
+       if (changes & AO_RADIO_MODE_BITS_PACKET_RX)
+               for (i = 0; i < sizeof (packet_rx_setup) / sizeof (packet_rx_setup[0]); i += 2)
+                       ao_radio_reg_write(packet_rx_setup[i], packet_rx_setup[i+1]);
+               
+       if (changes & AO_RADIO_MODE_BITS_RDF)
+               for (i = 0; i < sizeof (rdf_setup) / sizeof (rdf_setup[0]); i += 2)
+                       ao_radio_reg_write(rdf_setup[i], rdf_setup[i+1]);
+       ao_radio_mode = new_mode;
+}
+
+static const uint16_t radio_setup[] = {
+#include "ao_cc1120_CC1120.h"
+};
+
+static uint8_t ao_radio_configured = 0;
+
+static void
+ao_radio_setup(void)
+{
+       int     i;
+
+       ao_radio_strobe(CC1120_SRES);
+
+       for (i = 0; i < sizeof (radio_setup) / sizeof (radio_setup[0]); i += 2)
+               ao_radio_reg_write(radio_setup[i], radio_setup[i+1]);
+
+       ao_radio_mode = 0;
+
+       ao_config_get();
+
+       ao_radio_configured = 1;
+}
+
+static void
+ao_radio_get(uint8_t len)
+{
+       static uint32_t last_radio_setting;
+       static uint8_t  last_len;
+
+       ao_mutex_get(&ao_radio_mutex);
+       if (!ao_radio_configured)
+               ao_radio_setup();
+       if (ao_config.radio_setting != last_radio_setting) {
+               ao_radio_reg_write(CC1120_FREQ2, ao_config.radio_setting >> 16);
+               ao_radio_reg_write(CC1120_FREQ1, ao_config.radio_setting >> 8);
+               ao_radio_reg_write(CC1120_FREQ0, ao_config.radio_setting);
+               last_radio_setting = ao_config.radio_setting;
+       }
+       if (len != last_len) {
+               ao_radio_reg_write(CC1120_PKT_LEN, len);
+               last_len = len;
+       }
+}
+
+#define ao_radio_put() ao_mutex_put(&ao_radio_mutex)
+
+static void
+ao_rdf_start(uint8_t len)
+{
+       ao_radio_abort = 0;
+       ao_radio_get(len);
+
+       ao_radio_set_mode(AO_RADIO_MODE_RDF);
+       ao_radio_wake = 0;
+
+}
+
+static void
+ao_rdf_run(void)
+{
+       ao_radio_start_tx();
+
+       cli();
+       while (!ao_radio_wake && !ao_radio_abort)
+               ao_sleep(&ao_radio_wake);
+       sei();
+       if (!ao_radio_wake)
+               ao_radio_idle();
+       ao_radio_put();
+}
+
+void
+ao_radio_rdf(void)
+{
+       ao_rdf_start(AO_RADIO_RDF_LEN);
+
+       ao_radio_fifo_write_fixed(ao_radio_rdf_value, AO_RADIO_RDF_LEN);
+
+       ao_rdf_run();
+}
+
+void
+ao_radio_continuity(uint8_t c)
+{
+       uint8_t i;
+       uint8_t status;
+
+       ao_rdf_start(AO_RADIO_CONT_TOTAL_LEN);
+
+       status = ao_radio_fifo_write_start();
+       for (i = 0; i < 3; i++) {
+               ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
+               if (i < c)
+                       ao_radio_spi_send_fixed(ao_radio_rdf_value, AO_RADIO_CONT_TONE_LEN);
+               else
+                       ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_TONE_LEN);
+       }
+       ao_radio_spi_send_fixed(0x00, AO_RADIO_CONT_PAUSE_LEN);
+       status = ao_radio_fifo_write_stop(status);
+       (void) status;
+       ao_rdf_run();
+}
+
+void
+ao_radio_rdf_abort(void)
+{
+       ao_radio_abort = 1;
+       ao_wakeup(&ao_radio_wake);
+}
+
+static void
+ao_radio_test_cmd(void)
+{
+       uint8_t mode = 2;
+       uint8_t 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) {
+#if HAS_MONITOR
+               ao_monitor_disable();
+#endif
+#if PACKET_HAS_SLAVE
+               ao_packet_slave_stop();
+#endif
+               ao_radio_get(0xff);
+               ao_radio_strobe(CC1120_STX);
+#if CC1120_TRACE
+               { int t; 
+                       for (t = 0; t < 10; t++) {
+                               printf ("status: %02x\n", ao_radio_status());
+                               ao_delay(AO_MS_TO_TICKS(100));
+                       }
+               }
+#endif
+               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;
+#if HAS_MONITOR
+               ao_monitor_enable();
+#endif
+       }
+}
+
+void
+ao_radio_send(const void *d, uint8_t size)
+{
+       uint8_t         marc_status;
+       static uint8_t  encode[256];
+       uint8_t         *e = encode;
+       uint8_t         encode_len;
+       uint8_t         this_len;
+       uint8_t         started = 0;
+       uint8_t         fifo_space;
+
+       encode_len = ao_fec_encode(d, size, encode);
+
+       ao_radio_get(encode_len);
+
+       started = 0;
+       fifo_space = CC1120_FIFO_SIZE;
+       while (encode_len) {
+               this_len = encode_len;
+
+               if (this_len > fifo_space) {
+                       this_len = fifo_space;
+                       ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_BUF);
+               } else {
+                       ao_radio_set_mode(AO_RADIO_MODE_PACKET_TX_FINISH);
+               }
+
+               ao_radio_fifo_write(e, this_len);
+               e += this_len;
+               encode_len -= this_len;
+
+               if (!started) {
+                       ao_radio_start_tx();
+                       started = 1;
+               } else {
+                       ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+               }
+                       
+               do {
+                       ao_radio_wake = 0;
+                       cli();
+                       while (!ao_radio_wake)
+                               ao_sleep(&ao_radio_wake);
+                       sei();
+                       if (!encode_len)
+                               break;
+                       fifo_space = ao_radio_tx_fifo_space();
+               } while (!fifo_space);
+       }
+       ao_radio_put();
+}
+
+#define AO_RADIO_MAX_RECV      90
+
+static uint8_t rx_data[(AO_RADIO_MAX_RECV + 4) * 2 * 8];
+static uint16_t        rx_data_count;
+static uint16_t        rx_data_consumed;
+static uint16_t rx_data_cur;
+static uint8_t rx_ignore;
+static uint8_t rx_waiting;
+
+#if AO_PROFILE
+static uint32_t        rx_start_tick, rx_packet_tick, rx_done_tick, rx_last_done_tick;
+
+uint32_t       ao_rx_start_tick, ao_rx_packet_tick, ao_rx_done_tick, ao_rx_last_done_tick;
+
+#include <ao_profile.h>
+#endif
+
+static void
+ao_radio_rx_isr(void)
+{
+       uint8_t d;
+
+       d = stm_spi2.dr;
+       stm_spi2.dr = 0;
+       if (rx_ignore == 0) {
+               if (rx_data_cur >= rx_data_count)
+                       ao_exti_disable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+               else
+                       rx_data[rx_data_cur++] = d;
+               if (rx_waiting && rx_data_cur - rx_data_consumed >= AO_FEC_DECODE_BLOCK) {
+#if AO_PROFILE
+                       if (!rx_packet_tick)
+                               rx_packet_tick = ao_profile_tick();
+                       if (rx_data_cur < rx_data_count)
+                               return;
+#endif
+                       rx_waiting = 0;
+                       ao_wakeup(&ao_radio_wake);
+               }
+       } else {
+               --rx_ignore;
+       }
+}
+
+static uint16_t
+ao_radio_rx_wait(void)
+{
+       cli();
+       rx_waiting = 1;
+       while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK &&
+              !ao_radio_abort) {
+               ao_sleep(&ao_radio_wake);
+       }
+       rx_waiting = 0;
+       sei();
+       if (ao_radio_abort)
+               return 0;
+       rx_data_consumed += AO_FEC_DECODE_BLOCK;
+#if AO_PROFILE
+       return rx_data_cur - rx_data_consumed;
+#endif
+       return AO_FEC_DECODE_BLOCK;
+}
+
+uint8_t
+ao_radio_recv(__xdata void *d, uint8_t size)
+{
+       uint8_t         len;
+       uint16_t        i;
+       uint8_t         rssi;
+       uint8_t         ret;
+       static int been_here = 0;
+
+       size -= 2;                      /* status bytes */
+       if (size > AO_RADIO_MAX_RECV) {
+               ao_delay(AO_SEC_TO_TICKS(1));
+               return 0;
+       }
+#if AO_PROFILE
+       rx_start_tick = ao_profile_tick();
+       rx_packet_tick = 0;
+#endif
+       len = size + 2;                 /* CRC bytes */
+       len += 1 + ~(len & 1);          /* 1 or two pad bytes */
+       len *= 2;                       /* 1/2 rate convolution */
+       rx_data_count = len * 8;        /* bytes to bits */
+       rx_data_cur = 0;
+       rx_data_consumed = 0;
+       rx_ignore = 2;
+
+       ao_radio_abort = 0;
+       ao_radio_in_recv = 1;
+       /* configure interrupt pin */
+       ao_radio_get(len);
+       ao_radio_set_mode(AO_RADIO_MODE_PACKET_RX);
+
+       ao_radio_wake = 0;
+
+       stm_spi2.cr2 = 0;
+
+       /* clear any RXNE */
+       (void) stm_spi2.dr;
+
+       ao_exti_set_callback(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN, ao_radio_rx_isr);
+       ao_exti_enable(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN);
+
+       ao_radio_strobe(CC1120_SRX);
+
+       ao_radio_burst_read_start(CC1120_SOFT_RX_DATA_OUT);
+
+       ret = ao_fec_decode(rx_data, rx_data_count, d, size + 2, ao_radio_rx_wait);
+
+       ao_radio_burst_read_stop();
+
+       ao_radio_strobe(CC1120_SIDLE);
+
+       /* Convert from 'real' rssi to cc1111-style values */
+
+       rssi = AO_RADIO_FROM_RSSI(ao_radio_reg_read(CC1120_RSSI1));
+
+       ao_radio_put();
+
+       /* Store the received RSSI value; the crc-OK byte is already done */
+
+       ((uint8_t *) d)[size] = (uint8_t) rssi;
+
+       ao_radio_in_recv = 0;
+
+       if (ao_radio_abort)
+               ao_delay(1);
+
+#if AO_PROFILE
+       rx_last_done_tick = rx_done_tick;
+       rx_done_tick = ao_profile_tick();
+
+       ao_rx_start_tick = rx_start_tick;
+       ao_rx_packet_tick = rx_packet_tick;
+       ao_rx_done_tick = rx_done_tick;
+       ao_rx_last_done_tick = rx_last_done_tick;
+#endif
+
+       return ret;
+}
+
+
+#if CC1120_DEBUG
+static char *cc1120_state_name[] = {
+       [CC1120_STATUS_STATE_IDLE] = "IDLE",
+       [CC1120_STATUS_STATE_RX] = "RX",
+       [CC1120_STATUS_STATE_TX] = "TX",
+       [CC1120_STATUS_STATE_FSTXON] = "FSTXON",
+       [CC1120_STATUS_STATE_CALIBRATE] = "CALIBRATE",
+       [CC1120_STATUS_STATE_SETTLING] = "SETTLING",
+       [CC1120_STATUS_STATE_RX_FIFO_ERROR] = "RX_FIFO_ERROR",
+       [CC1120_STATUS_STATE_TX_FIFO_ERROR] = "TX_FIFO_ERROR",
+};
+
+struct ao_cc1120_reg {
+       uint16_t        addr;
+       char            *name;
+};
+
+const static struct ao_cc1120_reg ao_cc1120_reg[] = {
+       { .addr = CC1120_IOCFG3,        .name = "IOCFG3" },
+       { .addr = CC1120_IOCFG2,        .name = "IOCFG2" },
+       { .addr = CC1120_IOCFG1,        .name = "IOCFG1" },
+       { .addr = CC1120_IOCFG0,        .name = "IOCFG0" },
+       { .addr = CC1120_SYNC3, .name = "SYNC3" },
+       { .addr = CC1120_SYNC2, .name = "SYNC2" },
+       { .addr = CC1120_SYNC1, .name = "SYNC1" },
+       { .addr = CC1120_SYNC0, .name = "SYNC0" },
+       { .addr = CC1120_SYNC_CFG1,     .name = "SYNC_CFG1" },
+       { .addr = CC1120_SYNC_CFG0,     .name = "SYNC_CFG0" },
+       { .addr = CC1120_DEVIATION_M,   .name = "DEVIATION_M" },
+       { .addr = CC1120_MODCFG_DEV_E,  .name = "MODCFG_DEV_E" },
+       { .addr = CC1120_DCFILT_CFG,    .name = "DCFILT_CFG" },
+       { .addr = CC1120_PREAMBLE_CFG1, .name = "PREAMBLE_CFG1" },
+       { .addr = CC1120_PREAMBLE_CFG0, .name = "PREAMBLE_CFG0" },
+       { .addr = CC1120_FREQ_IF_CFG,   .name = "FREQ_IF_CFG" },
+       { .addr = CC1120_IQIC,  .name = "IQIC" },
+       { .addr = CC1120_CHAN_BW,       .name = "CHAN_BW" },
+       { .addr = CC1120_MDMCFG1,       .name = "MDMCFG1" },
+       { .addr = CC1120_MDMCFG0,       .name = "MDMCFG0" },
+       { .addr = CC1120_DRATE2,        .name = "DRATE2" },
+       { .addr = CC1120_DRATE1,        .name = "DRATE1" },
+       { .addr = CC1120_DRATE0,        .name = "DRATE0" },
+       { .addr = CC1120_AGC_REF,       .name = "AGC_REF" },
+       { .addr = CC1120_AGC_CS_THR,    .name = "AGC_CS_THR" },
+       { .addr = CC1120_AGC_GAIN_ADJUST,       .name = "AGC_GAIN_ADJUST" },
+       { .addr = CC1120_AGC_CFG3,      .name = "AGC_CFG3" },
+       { .addr = CC1120_AGC_CFG2,      .name = "AGC_CFG2" },
+       { .addr = CC1120_AGC_CFG1,      .name = "AGC_CFG1" },
+       { .addr = CC1120_AGC_CFG0,      .name = "AGC_CFG0" },
+       { .addr = CC1120_FIFO_CFG,      .name = "FIFO_CFG" },
+       { .addr = CC1120_DEV_ADDR,      .name = "DEV_ADDR" },
+       { .addr = CC1120_SETTLING_CFG,  .name = "SETTLING_CFG" },
+       { .addr = CC1120_FS_CFG,        .name = "FS_CFG" },
+       { .addr = CC1120_WOR_CFG1,      .name = "WOR_CFG1" },
+       { .addr = CC1120_WOR_CFG0,      .name = "WOR_CFG0" },
+       { .addr = CC1120_WOR_EVENT0_MSB,        .name = "WOR_EVENT0_MSB" },
+       { .addr = CC1120_WOR_EVENT0_LSB,        .name = "WOR_EVENT0_LSB" },
+       { .addr = CC1120_PKT_CFG2,      .name = "PKT_CFG2" },
+       { .addr = CC1120_PKT_CFG1,      .name = "PKT_CFG1" },
+       { .addr = CC1120_PKT_CFG0,      .name = "PKT_CFG0" },
+       { .addr = CC1120_RFEND_CFG1,    .name = "RFEND_CFG1" },
+       { .addr = CC1120_RFEND_CFG0,    .name = "RFEND_CFG0" },
+       { .addr = CC1120_PA_CFG2,       .name = "PA_CFG2" },
+       { .addr = CC1120_PA_CFG1,       .name = "PA_CFG1" },
+       { .addr = CC1120_PA_CFG0,       .name = "PA_CFG0" },
+       { .addr = CC1120_PKT_LEN,       .name = "PKT_LEN" },
+       { .addr = CC1120_IF_MIX_CFG,    .name = "IF_MIX_CFG" },
+       { .addr = CC1120_FREQOFF_CFG,   .name = "FREQOFF_CFG" },
+       { .addr = CC1120_TOC_CFG,       .name = "TOC_CFG" },
+       { .addr = CC1120_MARC_SPARE,    .name = "MARC_SPARE" },
+       { .addr = CC1120_ECG_CFG,       .name = "ECG_CFG" },
+       { .addr = CC1120_SOFT_TX_DATA_CFG,      .name = "SOFT_TX_DATA_CFG" },
+       { .addr = CC1120_EXT_CTRL,      .name = "EXT_CTRL" },
+       { .addr = CC1120_RCCAL_FINE,    .name = "RCCAL_FINE" },
+       { .addr = CC1120_RCCAL_COARSE,  .name = "RCCAL_COARSE" },
+       { .addr = CC1120_RCCAL_OFFSET,  .name = "RCCAL_OFFSET" },
+       { .addr = CC1120_FREQOFF1,      .name = "FREQOFF1" },
+       { .addr = CC1120_FREQOFF0,      .name = "FREQOFF0" },
+       { .addr = CC1120_FREQ2, .name = "FREQ2" },
+       { .addr = CC1120_FREQ1, .name = "FREQ1" },
+       { .addr = CC1120_FREQ0, .name = "FREQ0" },
+       { .addr = CC1120_IF_ADC2,       .name = "IF_ADC2" },
+       { .addr = CC1120_IF_ADC1,       .name = "IF_ADC1" },
+       { .addr = CC1120_IF_ADC0,       .name = "IF_ADC0" },
+       { .addr = CC1120_FS_DIG1,       .name = "FS_DIG1" },
+       { .addr = CC1120_FS_DIG0,       .name = "FS_DIG0" },
+       { .addr = CC1120_FS_CAL3,       .name = "FS_CAL3" },
+       { .addr = CC1120_FS_CAL2,       .name = "FS_CAL2" },
+       { .addr = CC1120_FS_CAL1,       .name = "FS_CAL1" },
+       { .addr = CC1120_FS_CAL0,       .name = "FS_CAL0" },
+       { .addr = CC1120_FS_CHP,        .name = "FS_CHP" },
+       { .addr = CC1120_FS_DIVTWO,     .name = "FS_DIVTWO" },
+       { .addr = CC1120_FS_DSM1,       .name = "FS_DSM1" },
+       { .addr = CC1120_FS_DSM0,       .name = "FS_DSM0" },
+       { .addr = CC1120_FS_DVC1,       .name = "FS_DVC1" },
+       { .addr = CC1120_FS_DVC0,       .name = "FS_DVC0" },
+       { .addr = CC1120_FS_LBI,        .name = "FS_LBI" },
+       { .addr = CC1120_FS_PFD,        .name = "FS_PFD" },
+       { .addr = CC1120_FS_PRE,        .name = "FS_PRE" },
+       { .addr = CC1120_FS_REG_DIV_CML,        .name = "FS_REG_DIV_CML" },
+       { .addr = CC1120_FS_SPARE,      .name = "FS_SPARE" },
+       { .addr = CC1120_FS_VCO4,       .name = "FS_VCO4" },
+       { .addr = CC1120_FS_VCO3,       .name = "FS_VCO3" },
+       { .addr = CC1120_FS_VCO2,       .name = "FS_VCO2" },
+       { .addr = CC1120_FS_VCO1,       .name = "FS_VCO1" },
+       { .addr = CC1120_FS_VCO0,       .name = "FS_VCO0" },
+       { .addr = CC1120_GBIAS6,        .name = "GBIAS6" },
+       { .addr = CC1120_GBIAS5,        .name = "GBIAS5" },
+       { .addr = CC1120_GBIAS4,        .name = "GBIAS4" },
+       { .addr = CC1120_GBIAS3,        .name = "GBIAS3" },
+       { .addr = CC1120_GBIAS2,        .name = "GBIAS2" },
+       { .addr = CC1120_GBIAS1,        .name = "GBIAS1" },
+       { .addr = CC1120_GBIAS0,        .name = "GBIAS0" },
+       { .addr = CC1120_IFAMP, .name = "IFAMP" },
+       { .addr = CC1120_LNA,   .name = "LNA" },
+       { .addr = CC1120_RXMIX, .name = "RXMIX" },
+       { .addr = CC1120_XOSC5, .name = "XOSC5" },
+       { .addr = CC1120_XOSC4, .name = "XOSC4" },
+       { .addr = CC1120_XOSC3, .name = "XOSC3" },
+       { .addr = CC1120_XOSC2, .name = "XOSC2" },
+       { .addr = CC1120_XOSC1, .name = "XOSC1" },
+       { .addr = CC1120_XOSC0, .name = "XOSC0" },
+       { .addr = CC1120_ANALOG_SPARE,  .name = "ANALOG_SPARE" },
+       { .addr = CC1120_PA_CFG3,       .name = "PA_CFG3" },
+       { .addr = CC1120_WOR_TIME1,     .name = "WOR_TIME1" },
+       { .addr = CC1120_WOR_TIME0,     .name = "WOR_TIME0" },
+       { .addr = CC1120_WOR_CAPTURE1,  .name = "WOR_CAPTURE1" },
+       { .addr = CC1120_WOR_CAPTURE0,  .name = "WOR_CAPTURE0" },
+       { .addr = CC1120_BIST,  .name = "BIST" },
+       { .addr = CC1120_DCFILTOFFSET_I1,       .name = "DCFILTOFFSET_I1" },
+       { .addr = CC1120_DCFILTOFFSET_I0,       .name = "DCFILTOFFSET_I0" },
+       { .addr = CC1120_DCFILTOFFSET_Q1,       .name = "DCFILTOFFSET_Q1" },
+       { .addr = CC1120_DCFILTOFFSET_Q0,       .name = "DCFILTOFFSET_Q0" },
+       { .addr = CC1120_IQIE_I1,       .name = "IQIE_I1" },
+       { .addr = CC1120_IQIE_I0,       .name = "IQIE_I0" },
+       { .addr = CC1120_IQIE_Q1,       .name = "IQIE_Q1" },
+       { .addr = CC1120_IQIE_Q0,       .name = "IQIE_Q0" },
+       { .addr = CC1120_RSSI1, .name = "RSSI1" },
+       { .addr = CC1120_RSSI0, .name = "RSSI0" },
+       { .addr = CC1120_MARCSTATE,     .name = "MARCSTATE" },
+       { .addr = CC1120_LQI_VAL,       .name = "LQI_VAL" },
+       { .addr = CC1120_PQT_SYNC_ERR,  .name = "PQT_SYNC_ERR" },
+       { .addr = CC1120_DEM_STATUS,    .name = "DEM_STATUS" },
+       { .addr = CC1120_FREQOFF_EST1,  .name = "FREQOFF_EST1" },
+       { .addr = CC1120_FREQOFF_EST0,  .name = "FREQOFF_EST0" },
+       { .addr = CC1120_AGC_GAIN3,     .name = "AGC_GAIN3" },
+       { .addr = CC1120_AGC_GAIN2,     .name = "AGC_GAIN2" },
+       { .addr = CC1120_AGC_GAIN1,     .name = "AGC_GAIN1" },
+       { .addr = CC1120_AGC_GAIN0,     .name = "AGC_GAIN0" },
+       { .addr = CC1120_SOFT_RX_DATA_OUT,      .name = "SOFT_RX_DATA_OUT" },
+       { .addr = CC1120_SOFT_TX_DATA_IN,       .name = "SOFT_TX_DATA_IN" },
+       { .addr = CC1120_ASK_SOFT_RX_DATA,      .name = "ASK_SOFT_RX_DATA" },
+       { .addr = CC1120_RNDGEN,        .name = "RNDGEN" },
+       { .addr = CC1120_MAGN2, .name = "MAGN2" },
+       { .addr = CC1120_MAGN1, .name = "MAGN1" },
+       { .addr = CC1120_MAGN0, .name = "MAGN0" },
+       { .addr = CC1120_ANG1,  .name = "ANG1" },
+       { .addr = CC1120_ANG0,  .name = "ANG0" },
+       { .addr = CC1120_CHFILT_I2,     .name = "CHFILT_I2" },
+       { .addr = CC1120_CHFILT_I1,     .name = "CHFILT_I1" },
+       { .addr = CC1120_CHFILT_I0,     .name = "CHFILT_I0" },
+       { .addr = CC1120_CHFILT_Q2,     .name = "CHFILT_Q2" },
+       { .addr = CC1120_CHFILT_Q1,     .name = "CHFILT_Q1" },
+       { .addr = CC1120_CHFILT_Q0,     .name = "CHFILT_Q0" },
+       { .addr = CC1120_GPIO_STATUS,   .name = "GPIO_STATUS" },
+       { .addr = CC1120_FSCAL_CTRL,    .name = "FSCAL_CTRL" },
+       { .addr = CC1120_PHASE_ADJUST,  .name = "PHASE_ADJUST" },
+       { .addr = CC1120_PARTNUMBER,    .name = "PARTNUMBER" },
+       { .addr = CC1120_PARTVERSION,   .name = "PARTVERSION" },
+       { .addr = CC1120_SERIAL_STATUS, .name = "SERIAL_STATUS" },
+       { .addr = CC1120_RX_STATUS,     .name = "RX_STATUS" },
+       { .addr = CC1120_TX_STATUS,     .name = "TX_STATUS" },
+       { .addr = CC1120_MARC_STATUS1,  .name = "MARC_STATUS1" },
+       { .addr = CC1120_MARC_STATUS0,  .name = "MARC_STATUS0" },
+       { .addr = CC1120_PA_IFAMP_TEST, .name = "PA_IFAMP_TEST" },
+       { .addr = CC1120_FSRF_TEST,     .name = "FSRF_TEST" },
+       { .addr = CC1120_PRE_TEST,      .name = "PRE_TEST" },
+       { .addr = CC1120_PRE_OVR,       .name = "PRE_OVR" },
+       { .addr = CC1120_ADC_TEST,      .name = "ADC_TEST" },
+       { .addr = CC1120_DVC_TEST,      .name = "DVC_TEST" },
+       { .addr = CC1120_ATEST, .name = "ATEST" },
+       { .addr = CC1120_ATEST_LVDS,    .name = "ATEST_LVDS" },
+       { .addr = CC1120_ATEST_MODE,    .name = "ATEST_MODE" },
+       { .addr = CC1120_XOSC_TEST1,    .name = "XOSC_TEST1" },
+       { .addr = CC1120_XOSC_TEST0,    .name = "XOSC_TEST0" },
+       { .addr = CC1120_RXFIRST,       .name = "RXFIRST" },
+       { .addr = CC1120_TXFIRST,       .name = "TXFIRST" },
+       { .addr = CC1120_RXLAST,        .name = "RXLAST" },
+       { .addr = CC1120_TXLAST,        .name = "TXLAST" },
+       { .addr = CC1120_NUM_TXBYTES,   .name = "NUM_TXBYTES" },
+       { .addr = CC1120_NUM_RXBYTES,   .name = "NUM_RXBYTES" },
+       { .addr = CC1120_FIFO_NUM_TXBYTES,      .name = "FIFO_NUM_TXBYTES" },
+       { .addr = CC1120_FIFO_NUM_RXBYTES,      .name = "FIFO_NUM_RXBYTES" },
+};
+
+#define AO_NUM_CC1120_REG      (sizeof ao_cc1120_reg / sizeof ao_cc1120_reg[0])
+
+static void ao_radio_show(void) {
+       uint8_t status = ao_radio_status();
+       int     i;
+
+       ao_radio_get(0xff);
+       status = ao_radio_status();
+       printf ("Status:   %02x\n", status);
+       printf ("CHIP_RDY: %d\n", (status >> CC1120_STATUS_CHIP_RDY) & 1);
+       printf ("STATE:    %s\n", cc1120_state_name[(status >> CC1120_STATUS_STATE) & CC1120_STATUS_STATE_MASK]);
+       printf ("MARC:     %02x\n", ao_radio_marc_status());
+
+       for (i = 0; i < AO_NUM_CC1120_REG; i++)
+               printf ("\t%02x %-20.20s\n", ao_radio_reg_read(ao_cc1120_reg[i].addr), ao_cc1120_reg[i].name);
+       ao_radio_put();
+}
+
+static void ao_radio_beep(void) {
+       ao_radio_rdf(RDF_PACKET_LEN);
+}
+
+static void ao_radio_packet(void) {
+       static const uint8_t packet[] = {
+#if 1
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+#else
+               3, 1, 2, 3
+#endif
+       };
+
+       ao_radio_send(packet, sizeof (packet));
+}
+
+void
+ao_radio_test_recv()
+{
+       uint8_t bytes[34];
+       uint8_t b;
+
+       if (ao_radio_recv(bytes, 34)) {
+               if (bytes[33] & 0x80)
+                       printf ("CRC OK");
+               else
+                       printf ("CRC BAD");
+               printf (" RSSI %d", AO_RSSI_FROM_RADIO(bytes[32]));
+               for (b = 0; b < 32; b++)
+                       printf (" %02x", bytes[b]);
+               printf ("\n");
+       }
+}
+
+#endif
+
+static const struct ao_cmds ao_radio_cmds[] = {
+       { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
+#if CC1120_DEBUG
+       { ao_radio_show,        "R\0Show CC1120 status" },
+       { ao_radio_beep,        "b\0Emit an RDF beacon" },
+       { ao_radio_packet,      "p\0Send a test packet" },
+       { ao_radio_test_recv,   "q\0Recv a test packet" },
+#endif
+       { 0, NULL }
+};
+
+void
+ao_radio_init(void)
+{
+       int     i;
+
+       ao_radio_configured = 0;
+       ao_spi_init_cs (AO_CC1120_SPI_CS_PORT, (1 << AO_CC1120_SPI_CS_PIN));
+
+       AO_CC1120_SPI_CS_PORT->bsrr = ((uint32_t) (1 << AO_CC1120_SPI_CS_PIN));
+       for (i = 0; i < 10000; i++) {
+               if ((SPI_2_PORT->idr & (1 << SPI_2_MISO_PIN)) == 0)
+                       break;
+       }
+       AO_CC1120_SPI_CS_PORT->bsrr = (1 << AO_CC1120_SPI_CS_PIN);
+       if (i == 10000)
+               ao_panic(AO_PANIC_SELF_TEST_CC1120);
+
+       /* Enable the EXTI interrupt for the appropriate pin */
+       ao_enable_port(AO_CC1120_INT_PORT);
+       ao_exti_setup(AO_CC1120_INT_PORT, AO_CC1120_INT_PIN,
+                     AO_EXTI_MODE_FALLING|AO_EXTI_PRIORITY_HIGH,
+                     ao_radio_tx_isr);
+
+       ao_cmd_register(&ao_radio_cmds[0]);
+}
diff --git a/src/drivers/ao_cc1120.h b/src/drivers/ao_cc1120.h
new file mode 100644 (file)
index 0000000..60b9621
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_CC1120_H_
+#define _AO_CC1120_H_
+
+#define CC1120_READ    (7)
+#define CC1120_BURST   (6)
+
+/* Register space */
+#define CC1120_IOCFG3          0x00
+#define  CC1120_IOCFG_GPIO_ATRAN       7
+#define  CC1120_IOCFG_GPIO_INV         6
+#define  CC1120_IOCFG_GPIO_CFG         0
+#define  CC1120_IOCFG_GPIO_CFG_RXFIFO_THR      0
+#define  CC1120_IOCFG_GPIO_CFG_RXFIFO_THR_PKT  1       
+#define  CC1120_IOCFG_GPIO_CFG_TXFIFO_THR      2
+#define  CC1120_IOCFG_GPIO_CFG_TXFIFO_THR_PKT  3
+#define  CC1120_IOCFG_GPIO_CFG_RXFIFO_OVERFLOW 4
+#define  CC1120_IOCFG_GPIO_CFG_TXFIFO_UNDERFLOW        5
+#define  CC1120_IOCFG_GPIO_CFG_PKT_SYNC_RXTX   6
+#define  CC1120_IOCFG_GPIO_CFG_CRC_OK          7
+#define  CC1120_IOCFG_GPIO_CFG_SERIAL_CLK      8
+#define  CC1120_IOCFG_GPIO_CFG_SERIAL_RX       9
+#define  CC1120_IOCFG_GPIO_CFG_PQT_REACHED     11
+#define  CC1120_IOCFG_GPIO_CFG_PQT_VALID       12
+#define  CC1120_IOCFG_GPIO_CFG_RSSI_VALID      13
+#define  CC1120_IOCFG_GPIO3_CFG_RSSI_UPDATE    14
+#define  CC1120_IOCFG_GPIO2_CFG_RSSI_UPDATE    14
+#define  CC1120_IOCFG_GPIO1_CFG_AGC_HOLD       14
+#define  CC1120_IOCFG_GPIO0_CFG_AGC_UPDATE     14
+#define  CC1120_IOCFG_GPIO3_CFG_CGA_STATUS     15
+#define  CC1120_IOCFG_GPIO2_CFG_TXONCCA_DONE   15
+#define  CC1120_IOCFG_GPIO1_CFG_CCA_STATUS     15
+#define  CC1120_IOCFG_GPIO0_CFG_TXONCCA_FAILED 15
+#define  CC1120_IOCFG_GPIO_CFG_CARRIER_SENSE_VALID     16
+#define  CC1120_IOCFG_GPIO_CFG_CARRIER_SENSE   17
+#define  CC1120_IOCFG_GPIO3_CFG_DSSS_CLK       18
+#define  CC1120_IOCFG_GPIO2_CFG_DSSS_DATA0     18
+#define  CC1120_IOCFG_GPIO1_CFG_DSSS_CLK       18
+#define  CC1120_IOCFG_GPIO0_CFG_DSSS_DATA1     18
+#define  CC1120_IOCFG_GPIO_CFG_PKT_CRC_OK      19
+#define  CC1120_IOCFG_GPIO_CFG_MARC_MCU_WAKEUP 20
+#define  CC1120_IOCFG_GPIO_CFG_SYNC_LOW0_HIGH1 21
+#define  CC1120_IOCFG_GPIO_CFG_LNA_PA_REG_PD   23
+#define  CC1120_IOCFG_GPIO_CFG_LNA_PD          24
+#define  CC1120_IOCFG_GPIO_CFG_PA_RD           25
+#define  CC1120_IOCFG_GPIO_CFG_RX0TX1_CFG      26
+#define  CC1120_IOCFG_GPIO_CFG_IMAGE_FOUND     28
+#define  CC1120_IOCFG_GPIO_CFG_CLKEN_SOFT      29
+#define  CC1120_IOCFG_GPIO_CFG_SOFT_TX_DATA_CLK        30
+#define  CC1120_IOCFG_GPIO_CFG_RSSI_STEP_FOUND 33
+#define  CC1120_IOCFG_GPIO_CFG_RSSI_STEP_EVENT 34
+#define  CC1120_IOCFG_GPIO_CFG_ANTENNA_SELECT  36
+#define  CC1120_IOCFG_GPIO_CFG_MARC_2PIN_STATUS1       37
+#define  CC1120_IOCFG_GPIO_CFG_MARC_2PIN_STATUS0       38
+#define  CC1120_IOCFG_GPIO2_CFG_TXFIFO_OVERFLOW                39
+#define  CC1120_IOCFG_GPIO0_CFG_RXFIFO_UNDERFLOW       39
+#define  CC1120_IOCFG_GPIO3_CFG_MAGN_VALID     40
+#define  CC1120_IOCFG_GPIO2_CFG_CHFILT_VALID   40
+#define  CC1120_IOCFG_GPIO1_CFG_RCC_CAL_VALID  40
+#define  CC1120_IOCFG_GPIO0_CFG_CHFILTER_STARTUP_VALID 40
+#define  CC1120_IOCFG_GPIO3_CFG_COLLISION_FOUND                41
+#define  CC1120_IOCFG_GPIO2_CFG_SYNC_EVENT             41
+#define  CC1120_IOCFG_GPIO1_CFG_COLLISION_FOUND                41
+#define  CC1120_IOCFG_GPIO0_CFG_COLLISION_EVENT                41
+#define  CC1120_IOCFG_GPIO_CFG_PA_RAMP_UP              42
+#define  CC1120_IOCFG_GPIO3_CFG_CRC_FAILED             43
+#define  CC1120_IOCFG_GPIO2_CFG_LENGTH_FAILED          43
+#define  CC1120_IOCFG_GPIO1_CFG_ADDR_FAILED            43
+#define  CC1120_IOCFG_GPIO0_CFG_UART_FRAMING_ERROR     43
+#define  CC1120_IOCFG_GPIO_CFG_AGC_STABLE_GAIN         44
+#define  CC1120_IOCFG_GPIO_CFG_AGC_UPDATE              45
+#define  CC1120_IOCFG_GPIO3_CFG_ADC_CLOCK              46
+#define  CC1120_IOCFG_GPIO2_CFG_ADC_Q_DATA_SAMPLE      46
+#define  CC1120_IOCFG_GPIO1_CFG_ADC_CLOCK              46
+#define  CC1120_IOCFG_GPIO0_CFG_ADC_I_DATA_SAMPLE      46
+#define  CC1120_IOCFG_GPIO_CFG_HIGHZ                   48
+#define  CC1120_IOCFG_GPIO_CFG_EXT_CLOCK               49
+#define  CC1120_IOCFG_GPIO_CFG_CHIP_RDY                        50
+#define  CC1120_IOCFG_GPIO_CFG_HW0                     51
+#define  CC1120_IOCFG_GPIO_CFG_CLOCK_32K               54
+#define  CC1120_IOCFG_GPIO_CFG_WOR_EVENT0              55
+#define  CC1120_IOCFG_GPIO_CFG_WOR_EVENT1              56
+#define  CC1120_IOCFG_GPIO_CFG_WOR_EVENT2              57
+#define  CC1120_IOCFG_GPIO_CFG_XOSC_STABLE             59
+#define  CC1120_IOCFG_GPIO_CFG_EXT_OSC_EN              60
+#define  CC1120_IOCFG_GPIO_CFG_MASK    0x3f
+
+#define CC1120_IOCFG3          0x00
+#define CC1120_IOCFG2          0x01
+#define CC1120_IOCFG1          0x02
+#define CC1120_IOCFG0          0x03
+#define CC1120_SYNC3           0x04
+#define CC1120_SYNC2           0x05
+#define CC1120_SYNC1           0x06
+#define CC1120_SYNC0           0x07
+#define CC1120_SYNC_CFG1       0x08
+#define  CC1120_SYNC_CFG1_DEM_CFG      5
+#define  CC1120_SYNC_CFG1_DEM_CFG_PQT_GATING_DISABLED  0
+#define  CC1120_SYNC_CFG1_DEM_CFG_PQT_GATING_ENABLED   2
+#define  CC1120_SYNC_CFG1_DEM_CFG_MASK                 0x7
+
+#define  CC1120_SYNC_CFG1_SYNC_THR     0
+#define  CC1120_SYNC_CFG1_SYNC_MASK                    0x1f
+
+#define CC1120_SYNC_CFG0       0x09
+#define  CC1120_SYNC_CFG0_SYNC_MODE    2
+#define  CC1120_SYNC_CFG0_SYNC_MODE_NONE               0
+#define  CC1120_SYNC_CFG0_SYNC_MODE_11_BITS            1
+#define  CC1120_SYNC_CFG0_SYNC_MODE_16_BITS            2
+#define  CC1120_SYNC_CFG0_SYNC_MODE_18_BITS            3
+#define  CC1120_SYNC_CFG0_SYNC_MODE_24_BITS            4
+#define  CC1120_SYNC_CFG0_SYNC_MODE_32_BITS            5
+#define  CC1120_SYNC_CFG0_SYNC_MODE_16H_BITS           6
+#define  CC1120_SYNC_CFG0_SYNC_MODE_16D_BITS           7
+#define  CC1120_SYNC_CFG0_SYNC_MODE_MASK               7
+#define  CC1120_SYNC_CFG0_SYNC_NUM_ERROR       0
+#define  CC1120_SYNC_CFG0_SYNC_NUM_ERROR_0             0
+#define  CC1120_SYNC_CFG0_SYNC_NUM_ERROR_2             1
+#define  CC1120_SYNC_CFG0_SYNC_NUM_ERROR_DISABLED      3
+#define  CC1120_SYNC_CFG0_SYNC_NUM_ERROR_MASK          3
+
+#define CC1120_DEVIATION_M     0x0a
+#define CC1120_MODCFG_DEV_E    0x0b
+#define CC1120_MODCFG_DEV_E_MODEM_MODE         6
+#define  CC1120_MODCFG_DEV_E_MODEM_MODE_NORMAL         0
+#define  CC1120_MODCFG_DEV_E_MODEM_MODE_DSSS_REPEAT    1
+#define  CC1120_MODCFG_DEV_E_MODEM_MODE_DSSS_PN                2
+#define  CC1120_MODCFG_DEV_E_MODEM_MODE_MASK           3
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT         3
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_2_FSK           0
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_2_GFSK          1
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_ASK_OOK         3
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_4_FSK           4
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_4_GFSK          5
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_SC_MSK_UNSHAPED 6
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_SC_MSK_SHAPED   7
+#define CC1120_MODCFG_DEV_E_MOD_FORMAT_MASK            7
+#define CC1120_MODCFG_DEV_E_DEV_E              0
+#define CC1120_MODCFG_DEV_E_DEV_E_MASK         7
+
+#define CC1120_DCFILT_CFG      0x0c
+#define CC1120_PREAMBLE_CFG1   0x0d
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE     2
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_NONE                0
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_0_5_BYTE    1
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_1_BYTE      2
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_1_5_BYTE    3
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_2_BYTES     4
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_3_BYTES     5
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_4_BYTES     6
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_5_BYTES     7
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_6_BYTES     8
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_7_BYTES     9
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_8_BYTES     10
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_12_BYTES    11
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_24_BYTES    12
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_30_BYTES    13
+#define  CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_MASK                0xf
+
+#define  CC1120_PREAMBLE_CFG1_PREAMBLE_WORD    0
+#define  CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_AA         0
+#define  CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_55         1
+#define  CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_33         2
+#define  CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_CC         3
+#define  CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_MASK       3
+
+#define CC1120_PREAMBLE_CFG0   0x0e
+#define  CC1120_PREAMBLE_CFG0_PQT_EN           5
+#define  CC1120_PREAMBLE_CFG0_PQT_VALID_TIMEOUT        4
+#define  CC1120_PREAMBLE_CFG0_PQT              0
+#define  CC1120_PREAMBLE_CFG0_PQT_MASK                 0xf
+
+#define CC1120_FREQ_IF_CFG     0x0f
+#define CC1120_IQIC            0x10
+#define CC1120_CHAN_BW         0x11
+#define CC1120_MDMCFG1         0x12
+#define  CC1120_MDMCFG1_CARRIER_SENSE_GATE     7
+#define  CC1120_MDMCFG1_FIFO_EN                        6
+#define  CC1120_MDMCFG1_MANCHESTER_EN          5
+#define  CC1120_MDMCFG1_INVERT_DATA_EN         4
+#define  CC1120_MDMCFG1_COLLISION_DETECT_EN    3
+#define  CC1120_MDMCFG1_DVGA_GAIN              1
+#define  CC1120_MDMCFG1_DVGA_GAIN_0                    0
+#define  CC1120_MDMCFG1_DVGA_GAIN_3                    1
+#define  CC1120_MDMCFG1_DVGA_GAIN_6                    2
+#define  CC1120_MDMCFG1_DVGA_GAIN_9                    3
+#define  CC1120_MDMCFG1_DVGA_GAIN_MASK                 3
+#define  CC1120_MDMCFG1_SINGLE_ADC_EN          0
+
+#define CC1120_MDMCFG0         0x13
+#define CC1120_DRATE2          0x14
+#define CC1120_DRATE2_DATARATE_E               4
+#define CC1120_DRATE2_DATARATE_E_MASK          0xf
+#define CC1120_DRATE2_DATARATE_M_19_16         0
+#define CC1120_DRATE2_DATARATE_M_19_16_MASK    0xf
+
+#define CC1120_DRATE1          0x15
+#define CC1120_DRATE0          0x16
+#define CC1120_AGC_REF         0x17
+#define CC1120_AGC_CS_THR      0x18
+#define CC1120_AGC_GAIN_ADJUST 0x19
+#define CC1120_AGC_CFG3                0x1a
+#define CC1120_AGC_CFG2                0x1b
+#define CC1120_AGC_CFG1                0x1c
+#define CC1120_AGC_CFG0                0x1d
+#define CC1120_FIFO_CFG                0x1e
+#define  CC1120_FIFO_CFG_CRC_AUTOFLUSH         7
+#define  CC1120_FIFO_CFG_FIFO_THR              0
+#define CC1120_DEV_ADDR                0x1f
+#define CC1120_SETTLING_CFG    0x20
+#define  CC1120_SETTLING_CFG_FS_AUTOCAL                3
+#define  CC1120_SETTLING_CFG_FS_AUTOCAL_NEVER          0
+#define  CC1120_SETTLING_CFG_FS_AUTOCAL_IDLE_TO_ON     1
+#define  CC1120_SETTLING_CFG_FS_AUTOCAL_ON_TO_IDLE     2
+#define  CC1120_SETTLING_CFG_FS_AUTOCAL_EVERY_4TH_TIME 3
+#define  CC1120_SETTLING_CFG_FS_AUTOCAL_MASK           3
+#define  CC1120_SETTLING_CFG_LOCK_TIME         1
+#define  CC1120_SETTLING_CFG_LOCK_TIME_50_20           0
+#define  CC1120_SETTLING_CFG_LOCK_TIME_70_30           1
+#define  CC1120_SETTLING_CFG_LOCK_TIME_100_40          2
+#define  CC1120_SETTLING_CFG_LOCK_TIME_150_60          3
+#define  CC1120_SETTLING_CFG_LOCK_TIME_MASK            3
+#define  CC1120_SETTLING_CFG_FSREG_TIME                0
+#define  CC1120_SETTLING_CFG_FSREG_TIME_30             0
+#define  CC1120_SETTLING_CFG_FSREG_TIME_60             1
+#define  CC1120_SETTLING_CFG_FSREG_TIME_MASK           1
+
+#define CC1120_FS_CFG          0x21
+#define  CC1120_FS_CFG_LOCK_EN                 4
+#define  CC1120_FS_CFG_FSD_BANDSELECT          0
+#define  CC1120_FS_CFG_FSD_BANDSELECT_820_960          2
+#define  CC1120_FS_CFG_FSD_BANDSELECT_410_480          4
+#define  CC1120_FS_CFG_FSD_BANDSELECT_273_320          6
+#define  CC1120_FS_CFG_FSD_BANDSELECT_205_240          8
+#define  CC1120_FS_CFG_FSD_BANDSELECT_164_192          10
+#define  CC1120_FS_CFG_FSD_BANDSELECT_136_160          11
+#define  CC1120_FS_CFG_FSD_BANDSELECT_MASK             0xf
+
+#define CC1120_WOR_CFG1                0x22
+#define CC1120_WOR_CFG0                0x23
+#define CC1120_WOR_EVENT0_MSB  0x24
+#define CC1120_WOR_EVENT0_LSB  0x25
+#define CC1120_PKT_CFG2                0x26
+#define  CC1120_PKT_CFG2_CCA_MODE      2
+#define  CC1120_PKT_CFG2_CCA_MODE_ALWAYS_CLEAR         0
+#define  CC1120_PKT_CFG2_CCA_MODE_RSSI_THRESHOLD       1
+#define  CC1120_PKT_CFG2_CCA_MODE_NOT_RECEIVING                2
+#define  CC1120_PKT_CFG2_CCA_MODE_RSSI_OR_NOT          3
+#define  CC1120_PKT_CFG2_CCA_MODE_RSSI_AND_ETSI_LBT    4
+#define  CC1120_PKT_CFG2_CCA_MODE_MASK                 7
+#define  CC1120_PKT_CFG2_PKT_FORMAT    0
+#define  CC1120_PKT_CFG2_PKT_FORMAT_NORMAL             0
+#define  CC1120_PKT_CFG2_PKT_FORMAT_SYNCHRONOUS_SERIAL 1
+#define  CC1120_PKT_CFG2_PKT_FORMAT_RANDOM             2
+#define  CC1120_PKT_CFG2_PKT_FORMAT_TRANSPARENT_SERIAL 3
+#define  CC1120_PKT_CFG2_PKT_FORMAT_MASK               3
+
+#define CC1120_PKT_CFG1                0x27
+#define  CC1120_PKT_CFG1_WHITE_DATA    6
+#define  CC1120_PKT_CFG1_ADDR_CHECK_CFG        4
+#define  CC1120_PKT_CFG1_ADDR_CHECK_CFG_NONE           0
+#define  CC1120_PKT_CFG1_ADDR_CHECK_CFG_CHECK          1
+#define  CC1120_PKT_CFG1_ADDR_CHECK_CFG_00_BROADCAST   2
+#define  CC1120_PKT_CFG1_ADDR_CHECK_CFG_00_FF_BROADCAST        3
+#define  CC1120_PKT_CFG1_ADDR_CHECK_CFG_MASK           3
+#define  CC1120_PKT_CFG1_CRC_CFG       2
+#define  CC1120_PKT_CFG1_CRC_CFG_DISABLED              0
+#define  CC1120_PKT_CFG1_CRC_CFG_CRC16_INIT_ONES       1
+#define  CC1120_PKT_CFG1_CRC_CFG_CRC16_INIT_ZEROS      2
+#define  CC1120_PKT_CFG1_CRC_CFG_MASK                  3
+#define  CC1120_PKT_CFG1_BYTE_SWAP_EN  1
+#define  CC1120_PKT_CFG1_APPEND_STATUS 0
+
+#define CC1120_PKT_CFG0                0x28
+#define  CC1120_PKT_CFG0_RESERVED7     7
+#define  CC1120_PKT_CFG0_LENGTH_CONFIG 5
+#define  CC1120_PKT_CFG0_LENGTH_CONFIG_FIXED           0
+#define  CC1120_PKT_CFG0_LENGTH_CONFIG_VARIABLE                1
+#define  CC1120_PKT_CFG0_LENGTH_CONFIG_INFINITE                2
+#define  CC1120_PKT_CFG0_LENGTH_CONFIG_VARIABLE_5LSB   3
+#define  CC1120_PKT_CFG0_LENGTH_CONFIG_MASK            3
+#define  CC1120_PKT_CFG0_PKG_BIT_LEN   2
+#define  CC1120_PKT_CFG0_PKG_BIT_LEN_MASK      7
+#define  CC1120_PKT_CFG0_UART_MODE_EN  1
+#define  CC1120_PKT_CFG0_UART_SWAP_EN  0
+
+#define CC1120_RFEND_CFG1      0x29
+#define CC1120_RFEND_CFG0      0x2a
+#define CC1120_PA_CFG2         0x2b
+#define CC1120_PA_CFG1         0x2c
+#define CC1120_PA_CFG0         0x2d
+#define CC1120_PKT_LEN         0x2e
+
+#define CC1120_EXTENDED        0x2f
+
+/* Command strobes */
+#define CC1120_SRES            0x30
+#define CC1120_SFSTXON         0x31
+#define CC1120_SXOFF           0x32
+#define CC1120_SCAL            0x33
+#define CC1120_SRX             0x34
+#define CC1120_STX             0x35
+#define CC1120_SIDLE           0x36
+#define CC1120_SAFC            0x37
+#define CC1120_SWOR            0x38
+#define CC1120_SPWD            0x39
+#define CC1120_SFRX            0x3a
+#define CC1120_SFTX            0x3b
+#define CC1120_SWORRST         0x3c
+#define CC1120_SNOP            0x3d
+
+#define CC1120_DIRECT_FIFO     0x3e
+#define CC1120_FIFO            0x3f
+
+#define CC1120_FIFO_SIZE       128
+
+/* Extended register space */
+
+#define CC1120_EXTENDED_BIT    0x8000
+
+#define CC1120_IS_EXTENDED(r)  ((r) & CC1120_EXTENDED_BIT)
+
+#define CC1120_IF_MIX_CFG      (CC1120_EXTENDED_BIT | 0x00)
+#define CC1120_FREQOFF_CFG     (CC1120_EXTENDED_BIT | 0x01)
+#define CC1120_TOC_CFG         (CC1120_EXTENDED_BIT | 0x02)
+#define CC1120_MARC_SPARE      (CC1120_EXTENDED_BIT | 0x03)
+#define CC1120_ECG_CFG         (CC1120_EXTENDED_BIT | 0x04)
+#define CC1120_SOFT_TX_DATA_CFG        (CC1120_EXTENDED_BIT | 0x05)
+#define CC1120_EXT_CTRL                (CC1120_EXTENDED_BIT | 0x06)
+#define CC1120_RCCAL_FINE      (CC1120_EXTENDED_BIT | 0x07)
+#define CC1120_RCCAL_COARSE    (CC1120_EXTENDED_BIT | 0x08)
+#define CC1120_RCCAL_OFFSET    (CC1120_EXTENDED_BIT | 0x09)
+#define CC1120_FREQOFF1                (CC1120_EXTENDED_BIT | 0x0A)
+#define CC1120_FREQOFF0                (CC1120_EXTENDED_BIT | 0x0B)
+#define CC1120_FREQ2           (CC1120_EXTENDED_BIT | 0x0C)
+#define CC1120_FREQ1           (CC1120_EXTENDED_BIT | 0x0D)
+#define CC1120_FREQ0           (CC1120_EXTENDED_BIT | 0x0E)
+#define CC1120_IF_ADC2         (CC1120_EXTENDED_BIT | 0x0F)
+#define CC1120_IF_ADC1         (CC1120_EXTENDED_BIT | 0x10)
+#define CC1120_IF_ADC0         (CC1120_EXTENDED_BIT | 0x11)
+#define CC1120_FS_DIG1         (CC1120_EXTENDED_BIT | 0x12)
+#define CC1120_FS_DIG0         (CC1120_EXTENDED_BIT | 0x13)
+#define CC1120_FS_CAL3         (CC1120_EXTENDED_BIT | 0x14)
+#define CC1120_FS_CAL2         (CC1120_EXTENDED_BIT | 0x15)
+#define CC1120_FS_CAL1         (CC1120_EXTENDED_BIT | 0x16)
+#define CC1120_FS_CAL0         (CC1120_EXTENDED_BIT | 0x17)
+#define CC1120_FS_CHP          (CC1120_EXTENDED_BIT | 0x18)
+#define CC1120_FS_DIVTWO       (CC1120_EXTENDED_BIT | 0x19)
+#define CC1120_FS_DSM1         (CC1120_EXTENDED_BIT | 0x1A)
+#define CC1120_FS_DSM0         (CC1120_EXTENDED_BIT | 0x1B)
+#define CC1120_FS_DVC1         (CC1120_EXTENDED_BIT | 0x1C)
+#define CC1120_FS_DVC0         (CC1120_EXTENDED_BIT | 0x1D)
+#define CC1120_FS_LBI          (CC1120_EXTENDED_BIT | 0x1E)
+#define CC1120_FS_PFD          (CC1120_EXTENDED_BIT | 0x1F)
+#define CC1120_FS_PRE          (CC1120_EXTENDED_BIT | 0x20)
+#define CC1120_FS_REG_DIV_CML  (CC1120_EXTENDED_BIT | 0x21)
+#define CC1120_FS_SPARE                (CC1120_EXTENDED_BIT | 0x22)
+#define CC1120_FS_VCO4         (CC1120_EXTENDED_BIT | 0x23)
+#define CC1120_FS_VCO3         (CC1120_EXTENDED_BIT | 0x24)
+#define CC1120_FS_VCO2         (CC1120_EXTENDED_BIT | 0x25)
+#define CC1120_FS_VCO1         (CC1120_EXTENDED_BIT | 0x26)
+#define CC1120_FS_VCO0         (CC1120_EXTENDED_BIT | 0x27)
+#define CC1120_GBIAS6          (CC1120_EXTENDED_BIT | 0x28)
+#define CC1120_GBIAS5          (CC1120_EXTENDED_BIT | 0x29)
+#define CC1120_GBIAS4          (CC1120_EXTENDED_BIT | 0x2A)
+#define CC1120_GBIAS3          (CC1120_EXTENDED_BIT | 0x2B)
+#define CC1120_GBIAS2          (CC1120_EXTENDED_BIT | 0x2C)
+#define CC1120_GBIAS1          (CC1120_EXTENDED_BIT | 0x2D)
+#define CC1120_GBIAS0          (CC1120_EXTENDED_BIT | 0x2E)
+#define CC1120_IFAMP           (CC1120_EXTENDED_BIT | 0x2F)
+#define CC1120_LNA             (CC1120_EXTENDED_BIT | 0x30)
+#define CC1120_RXMIX           (CC1120_EXTENDED_BIT | 0x31)
+#define CC1120_XOSC5           (CC1120_EXTENDED_BIT | 0x32)
+#define CC1120_XOSC4           (CC1120_EXTENDED_BIT | 0x33)
+#define CC1120_XOSC3           (CC1120_EXTENDED_BIT | 0x34)
+#define CC1120_XOSC2           (CC1120_EXTENDED_BIT | 0x35)
+#define CC1120_XOSC1           (CC1120_EXTENDED_BIT | 0x36)
+#define CC1120_XOSC0           (CC1120_EXTENDED_BIT | 0x37)
+#define CC1120_ANALOG_SPARE    (CC1120_EXTENDED_BIT | 0x38)
+#define CC1120_PA_CFG3         (CC1120_EXTENDED_BIT | 0x39)
+#define CC1120_WOR_TIME1       (CC1120_EXTENDED_BIT | 0x64)
+#define CC1120_WOR_TIME0       (CC1120_EXTENDED_BIT | 0x65)
+#define CC1120_WOR_CAPTURE1    (CC1120_EXTENDED_BIT | 0x66)
+#define CC1120_WOR_CAPTURE0    (CC1120_EXTENDED_BIT | 0x67)
+#define CC1120_BIST            (CC1120_EXTENDED_BIT | 0x68)
+#define CC1120_DCFILTOFFSET_I1 (CC1120_EXTENDED_BIT | 0x69)
+#define CC1120_DCFILTOFFSET_I0 (CC1120_EXTENDED_BIT | 0x6A)
+#define CC1120_DCFILTOFFSET_Q1 (CC1120_EXTENDED_BIT | 0x6B)
+#define CC1120_DCFILTOFFSET_Q0 (CC1120_EXTENDED_BIT | 0x6C)
+#define CC1120_IQIE_I1         (CC1120_EXTENDED_BIT | 0x6D)
+#define CC1120_IQIE_I0         (CC1120_EXTENDED_BIT | 0x6E)
+#define CC1120_IQIE_Q1         (CC1120_EXTENDED_BIT | 0x6f)
+#define CC1120_IQIE_Q0         (CC1120_EXTENDED_BIT | 0x70)
+#define CC1120_RSSI1           (CC1120_EXTENDED_BIT | 0x71)
+#define CC1120_RSSI0           (CC1120_EXTENDED_BIT | 0x72)
+#define CC1120_MARCSTATE       (CC1120_EXTENDED_BIT | 0x73)
+#define CC1120_LQI_VAL         (CC1120_EXTENDED_BIT | 0x74)
+#define CC1120_PQT_SYNC_ERR    (CC1120_EXTENDED_BIT | 0x75)
+#define CC1120_DEM_STATUS      (CC1120_EXTENDED_BIT | 0x76)
+#define CC1120_FREQOFF_EST1    (CC1120_EXTENDED_BIT | 0x77)
+#define CC1120_FREQOFF_EST0    (CC1120_EXTENDED_BIT | 0x78)
+#define CC1120_AGC_GAIN3       (CC1120_EXTENDED_BIT | 0x79)
+#define CC1120_AGC_GAIN2       (CC1120_EXTENDED_BIT | 0x7a)
+#define CC1120_AGC_GAIN1       (CC1120_EXTENDED_BIT | 0x7b)
+#define CC1120_AGC_GAIN0       (CC1120_EXTENDED_BIT | 0x7c)
+#define CC1120_SOFT_RX_DATA_OUT        (CC1120_EXTENDED_BIT | 0x7d)
+#define CC1120_SOFT_TX_DATA_IN (CC1120_EXTENDED_BIT | 0x7e)
+#define CC1120_ASK_SOFT_RX_DATA        (CC1120_EXTENDED_BIT | 0x7f)
+#define CC1120_RNDGEN          (CC1120_EXTENDED_BIT | 0x80)
+#define CC1120_MAGN2           (CC1120_EXTENDED_BIT | 0x81)
+#define CC1120_MAGN1           (CC1120_EXTENDED_BIT | 0x82)
+#define CC1120_MAGN0           (CC1120_EXTENDED_BIT | 0x83)
+#define CC1120_ANG1            (CC1120_EXTENDED_BIT | 0x84)
+#define CC1120_ANG0            (CC1120_EXTENDED_BIT | 0x85)
+#define CC1120_CHFILT_I2       (CC1120_EXTENDED_BIT | 0x86)
+#define CC1120_CHFILT_I1       (CC1120_EXTENDED_BIT | 0x87)
+#define CC1120_CHFILT_I0       (CC1120_EXTENDED_BIT | 0x88)
+#define CC1120_CHFILT_Q2       (CC1120_EXTENDED_BIT | 0x89)
+#define CC1120_CHFILT_Q1       (CC1120_EXTENDED_BIT | 0x8a)
+#define CC1120_CHFILT_Q0       (CC1120_EXTENDED_BIT | 0x8b)
+#define CC1120_GPIO_STATUS     (CC1120_EXTENDED_BIT | 0x8c)
+#define CC1120_FSCAL_CTRL      (CC1120_EXTENDED_BIT | 0x8d)
+#define CC1120_PHASE_ADJUST    (CC1120_EXTENDED_BIT | 0x8e)
+#define CC1120_PARTNUMBER      (CC1120_EXTENDED_BIT | 0x8f)
+#define CC1120_PARTVERSION     (CC1120_EXTENDED_BIT | 0x90)
+#define CC1120_SERIAL_STATUS   (CC1120_EXTENDED_BIT | 0x91)
+#define CC1120_RX_STATUS       (CC1120_EXTENDED_BIT | 0x92)
+#define CC1120_TX_STATUS       (CC1120_EXTENDED_BIT | 0x93)
+#define CC1120_MARC_STATUS1    (CC1120_EXTENDED_BIT | 0x94)
+# define CC1120_MARC_STATUS1_NO_FAILURE                0
+# define CC1120_MARC_STATUS1_RX_TIMEOUT                1
+# define CC1120_MARC_STATUS1_RX_TERMINATION    2
+# define CC1120_MARC_STATUS1_EWOR_SYNC_LOST    3
+# define CC1120_MARC_STATUS1_MAXIMUM_LENGTH    4
+# define CC1120_MARC_STATUS1_ADDRESS           5
+# define CC1120_MARC_STATUS1_CRC               6
+# define CC1120_MARC_STATUS1_TX_FIFO_OVERFLOW  7
+# define CC1120_MARC_STATUS1_TX_FIFO_UNDERFLOW 8
+# define CC1120_MARC_STATUS1_RX_FIFO_OVERFLOW  9
+# define CC1120_MARC_STATUS1_RX_FIFO_UNDERFLOW 10
+# define CC1120_MARC_STATUS1_TX_ON_CCA_FAILED  11
+# define CC1120_MARC_STATUS1_TX_FINISHED       0x40
+# define CC1120_MARC_STATUS1_RX_FINISHED       0x80
+#define CC1120_MARC_STATUS0    (CC1120_EXTENDED_BIT | 0x95)
+#define CC1120_PA_IFAMP_TEST   (CC1120_EXTENDED_BIT | 0x96)
+#define CC1120_FSRF_TEST       (CC1120_EXTENDED_BIT | 0x97)
+#define CC1120_PRE_TEST                (CC1120_EXTENDED_BIT | 0x98)
+#define CC1120_PRE_OVR         (CC1120_EXTENDED_BIT | 0x99)
+#define CC1120_ADC_TEST                (CC1120_EXTENDED_BIT | 0x9a)
+#define CC1120_DVC_TEST                (CC1120_EXTENDED_BIT | 0x9b)
+#define CC1120_ATEST           (CC1120_EXTENDED_BIT | 0x9c)
+#define CC1120_ATEST_LVDS      (CC1120_EXTENDED_BIT | 0x9d)
+#define CC1120_ATEST_MODE      (CC1120_EXTENDED_BIT | 0x9e)
+#define CC1120_XOSC_TEST1      (CC1120_EXTENDED_BIT | 0x9f)
+#define CC1120_XOSC_TEST0      (CC1120_EXTENDED_BIT | 0xa0)
+#define CC1120_RXFIRST         (CC1120_EXTENDED_BIT | 0xd2)
+#define CC1120_TXFIRST         (CC1120_EXTENDED_BIT | 0xd3)
+#define CC1120_RXLAST          (CC1120_EXTENDED_BIT | 0xd4)
+#define CC1120_TXLAST          (CC1120_EXTENDED_BIT | 0xd5)
+#define CC1120_NUM_TXBYTES     (CC1120_EXTENDED_BIT | 0xd6)
+#define CC1120_NUM_RXBYTES     (CC1120_EXTENDED_BIT | 0xd7)
+#define CC1120_FIFO_NUM_TXBYTES        (CC1120_EXTENDED_BIT | 0xd8)
+#define CC1120_FIFO_NUM_RXBYTES        (CC1120_EXTENDED_BIT | 0xd9)
+
+/* Status byte */
+#define CC1120_STATUS_CHIP_RDY 7
+#define CC1120_STATUS_STATE    4
+#define  CC1120_STATUS_STATE_IDLE              0
+#define  CC1120_STATUS_STATE_RX                        1
+#define  CC1120_STATUS_STATE_TX                        2
+#define  CC1120_STATUS_STATE_FSTXON            3
+#define  CC1120_STATUS_STATE_CALIBRATE         4
+#define  CC1120_STATUS_STATE_SETTLING          5
+#define  CC1120_STATUS_STATE_RX_FIFO_ERROR     6
+#define  CC1120_STATUS_STATE_TX_FIFO_ERROR     7
+#define  CC1120_STATUS_STATE_MASK              7
+
+#endif /* _AO_CC1120_H_ */
diff --git a/src/drivers/ao_cc1120_CC1120.h b/src/drivers/ao_cc1120_CC1120.h
new file mode 100644 (file)
index 0000000..44cca93
--- /dev/null
@@ -0,0 +1,210 @@
+/* RX filter BW = 100.000000 */\r
+/* Address config = No address check */\r
+/* Packet length = 255 */\r
+/* Symbol rate = 38.3606 */\r
+/* PA ramping = false */\r
+/* Carrier frequency = 434.549988 */\r
+/* Bit rate = 38.3606 */\r
+/* Whitening = true */\r
+/* Manchester enable = false */\r
+/* Modulation format = 2-GFSK */\r
+/* Packet length mode = Variable */\r
+/* Device address = 0 */\r
+/* TX power = 15 */\r
+/* Deviation = 20.507812 */\r
+/***************************************************************\r
+ *  SmartRF Studio(tm) Export\r
+ *\r
+ *  Radio register settings specifed with address, value\r
+ *\r
+ *  RF device: CC1120\r
+ *\r
+ ***************************************************************/\r
+\r
+        CC1120_SYNC3,                          0xD3,       /* Sync Word Configuration [31:24] */\r
+        CC1120_SYNC2,                          0x91,       /* Sync Word Configuration [23:16] */\r
+        CC1120_SYNC1,                          0xD3,       /* Sync Word Configuration [15:8] */\r
+        CC1120_SYNC0,                          0x91,       /* Sync Word Configuration [7:0] */\r
+\r
+        CC1120_SYNC_CFG1,                                 /* Sync Word Detection Configuration */\r
+               (CC1120_SYNC_CFG1_DEM_CFG_PQT_GATING_ENABLED << CC1120_SYNC_CFG1_DEM_CFG) |\r
+               (0x07 << CC1120_SYNC_CFG1_SYNC_THR),\r
+        CC1120_SYNC_CFG0,\r
+               (CC1120_SYNC_CFG0_SYNC_MODE_16_BITS << CC1120_SYNC_CFG0_SYNC_MODE) |\r
+               (CC1120_SYNC_CFG0_SYNC_NUM_ERROR_2 << CC1120_SYNC_CFG0_SYNC_NUM_ERROR),\r
+        CC1120_DCFILT_CFG,                     0x1c,       /* Digital DC Removal Configuration */\r
+        CC1120_PREAMBLE_CFG1,                             /* Preamble Length Configuration */\r
+               (CC1120_PREAMBLE_CFG1_NUM_PREAMBLE_4_BYTES << CC1120_PREAMBLE_CFG1_NUM_PREAMBLE) |\r
+               (CC1120_PREAMBLE_CFG1_PREAMBLE_WORD_AA << CC1120_PREAMBLE_CFG1_PREAMBLE_WORD),\r
+        CC1120_PREAMBLE_CFG0,\r
+               (1 << CC1120_PREAMBLE_CFG0_PQT_EN) |\r
+               (0x6 << CC1120_PREAMBLE_CFG0_PQT),\r
+        CC1120_FREQ_IF_CFG,                    0x40,       /* RX Mixer Frequency Configuration */\r
+        CC1120_IQIC,                           0x46,       /* Digital Image Channel Compensation Configuration */\r
+        CC1120_CHAN_BW,                        0x02,       /* Channel Filter Configuration */\r
+\r
+        CC1120_MDMCFG1,                                   /* General Modem Parameter Configuration */\r
+               (0 << CC1120_MDMCFG1_CARRIER_SENSE_GATE) |\r
+               (1 << CC1120_MDMCFG1_FIFO_EN) |\r
+               (0 << CC1120_MDMCFG1_MANCHESTER_EN) |\r
+               (0 << CC1120_MDMCFG1_INVERT_DATA_EN) |\r
+               (0 << CC1120_MDMCFG1_COLLISION_DETECT_EN) |\r
+               (CC1120_MDMCFG1_DVGA_GAIN_9 << CC1120_MDMCFG1_DVGA_GAIN) |\r
+               (0 << CC1120_MDMCFG1_SINGLE_ADC_EN),\r
+        CC1120_MDMCFG0,                        0x05,       /* General Modem Parameter Configuration */\r
+\r
+        CC1120_AGC_REF,                        0x20,       /* AGC Reference Level Configuration */\r
+        CC1120_AGC_CS_THR,                     0x19,       /* Carrier Sense Threshold Configuration */\r
+        CC1120_AGC_GAIN_ADJUST,                0x00,       /* RSSI Offset Configuration */\r
+        CC1120_AGC_CFG3,                       0x91,       /* AGC Configuration */\r
+        CC1120_AGC_CFG2,                       0x20,       /* AGC Configuration */\r
+        CC1120_AGC_CFG1,                       0xa9,       /* AGC Configuration */\r
+        CC1120_AGC_CFG0,                       0xcf,       /* AGC Configuration */\r
+        CC1120_FIFO_CFG,                                  /* FIFO Configuration */\r
+               (0 << CC1120_FIFO_CFG_CRC_AUTOFLUSH) |\r
+               (0x40 << CC1120_FIFO_CFG_FIFO_THR),\r
+        CC1120_DEV_ADDR,                       0x00,       /* Device Address Configuration */\r
+        CC1120_SETTLING_CFG,                              /* Frequency Synthesizer Calibration and Settling Configuration */\r
+               (CC1120_SETTLING_CFG_FS_AUTOCAL_IDLE_TO_ON << CC1120_SETTLING_CFG_FS_AUTOCAL) |\r
+               (CC1120_SETTLING_CFG_LOCK_TIME_50_20 << CC1120_SETTLING_CFG_LOCK_TIME) |\r
+               (CC1120_SETTLING_CFG_FSREG_TIME_60 << CC1120_SETTLING_CFG_FSREG_TIME),\r
+        CC1120_FS_CFG,                                    /* Frequency Synthesizer Configuration */\r
+               (1 << CC1120_FS_CFG_LOCK_EN) |\r
+               (CC1120_FS_CFG_FSD_BANDSELECT_410_480 << CC1120_FS_CFG_FSD_BANDSELECT),\r
+        CC1120_WOR_CFG1,                       0x08,       /* eWOR Configuration, Reg 1 */\r
+        CC1120_WOR_CFG0,                       0x21,       /* eWOR Configuration, Reg 0 */\r
+        CC1120_WOR_EVENT0_MSB,                 0x00,       /* Event 0 Configuration */\r
+        CC1120_WOR_EVENT0_LSB,                 0x00,       /* Event 0 Configuration */\r
+#if 0\r
+        CC1120_PKT_CFG2,                       0x04,       /* Packet Configuration, Reg 2 */\r
+        CC1120_PKT_CFG1,                       0x45,       /* Packet Configuration, Reg 1 */\r
+        CC1120_PKT_CFG0,                       0x00,       /* Packet Configuration, Reg 0 */\r
+#endif\r
+        CC1120_RFEND_CFG1,                     0x0f,       /* RFEND Configuration, Reg 1 */\r
+        CC1120_RFEND_CFG0,                     0x00,       /* RFEND Configuration, Reg 0 */\r
+       //        CC1120_PA_CFG2,                        0x3f,       /* Power Amplifier Configuration, Reg 2 */\r
+       CC1120_PA_CFG2,                        0x3f,       /* Power Amplifier Configuration, Reg 2 */\r
+        CC1120_PA_CFG1,                        0x56,       /* Power Amplifier Configuration, Reg 1 */\r
+        CC1120_PA_CFG0,                        0x7b,       /* Power Amplifier Configuration, Reg 0 */\r
+        CC1120_PKT_LEN,                        0xff,       /* Packet Length Configuration */\r
+        CC1120_IF_MIX_CFG,                     0x00,       /* IF Mix Configuration */\r
+        CC1120_FREQOFF_CFG,                    0x22,       /* Frequency Offset Correction Configuration */\r
+        CC1120_TOC_CFG,                        0x0b,       /* Timing Offset Correction Configuration */\r
+        CC1120_MARC_SPARE,                     0x00,       /* MARC Spare */\r
+        CC1120_ECG_CFG,                        0x00,       /* External Clock Frequency Configuration */\r
+        CC1120_SOFT_TX_DATA_CFG,               0x00,       /* Soft TX Data Configuration */\r
+        CC1120_EXT_CTRL,                       0x00,       /* External Control Configuration */\r
+        CC1120_RCCAL_FINE,                     0x00,       /* RC Oscillator Calibration (fine) */\r
+        CC1120_RCCAL_COARSE,                   0x00,       /* RC Oscillator Calibration (coarse) */\r
+        CC1120_RCCAL_OFFSET,                   0x00,       /* RC Oscillator Calibration Clock Offset */\r
+        CC1120_FREQOFF1,                       0x00,       /* Frequency Offset (MSB) */\r
+        CC1120_FREQOFF0,                       0x00,       /* Frequency Offset (LSB) */\r
+        CC1120_IF_ADC2,                        0x02,       /* Analog to Digital Converter Configuration, Reg 2 */\r
+        CC1120_IF_ADC1,                        0xa6,       /* Analog to Digital Converter Configuration, Reg 1 */\r
+        CC1120_IF_ADC0,                        0x04,       /* Analog to Digital Converter Configuration, Reg 0 */\r
+        CC1120_FS_DIG1,                        0x00,       /*  */\r
+        CC1120_FS_DIG0,                        0x5f,       /*  */\r
+        CC1120_FS_CAL3,                        0x00,       /*  */\r
+        CC1120_FS_CAL2,                        0x20,       /*  */\r
+        CC1120_FS_CAL1,                        0x40,       /*  */\r
+        CC1120_FS_CAL0,                        0x0e,       /*  */\r
+        CC1120_FS_CHP,                         0x28,       /* Charge Pump Configuration */\r
+        CC1120_FS_DIVTWO,                      0x03,       /* Divide by 2 */\r
+        CC1120_FS_DSM1,                        0x00,       /* Digital Synthesizer Module Configuration, Reg 1 */\r
+        CC1120_FS_DSM0,                        0x33,       /* Digital Synthesizer Module Configuration, Reg 0 */\r
+        CC1120_FS_DVC1,                        0xff,       /* Divider Chain Configuration, Reg 1 */\r
+        CC1120_FS_DVC0,                        0x17,       /* Divider Chain Configuration, Reg 0 */\r
+        CC1120_FS_LBI,                         0x00,       /* Local Bias Configuration */\r
+        CC1120_FS_PFD,                         0x50,       /* Phase Frequency Detector Configuration */\r
+        CC1120_FS_PRE,                         0x6e,       /* Prescaler Configuration */\r
+        CC1120_FS_REG_DIV_CML,                 0x14,       /*  */\r
+        CC1120_FS_SPARE,                       0xac,       /*  */\r
+        CC1120_FS_VCO4,                        0x14,       /* VCO Configuration, Reg 4 */\r
+        CC1120_FS_VCO3,                        0x00,       /* VCO Configuration, Reg 3 */\r
+        CC1120_FS_VCO2,                        0x00,       /* VCO Configuration, Reg 2 */\r
+        CC1120_FS_VCO1,                        0x00,       /* VCO Configuration, Reg 1 */\r
+        CC1120_FS_VCO0,                        0xb4,       /* VCO Configuration, Reg 0 */\r
+        CC1120_GBIAS6,                         0x00,       /* Global Bias Configuration, Reg 6 */\r
+        CC1120_GBIAS5,                         0x02,       /* Global Bias Configuration, Reg 5 */\r
+        CC1120_GBIAS4,                         0x00,       /* Global Bias Configuration, Reg 4 */\r
+        CC1120_GBIAS3,                         0x00,       /* Global Bias Configuration, Reg 3 */\r
+        CC1120_GBIAS2,                         0x10,       /* Global Bias Configuration, Reg 2 */\r
+        CC1120_GBIAS1,                         0x00,       /* Global Bias Configuration, Reg 1 */\r
+        CC1120_GBIAS0,                         0x00,       /* Global Bias Configuration, Reg 0 */\r
+        CC1120_IFAMP,                          0x01,       /* Intermediate Frequency Amplifier Configuration */\r
+        CC1120_LNA,                            0x01,       /* Low Noise Amplifier Configuration */\r
+        CC1120_RXMIX,                          0x01,       /* RX Mixer Configuration */\r
+        CC1120_XOSC5,                          0x0e,       /* Crystal Oscillator Configuration, Reg 5 */\r
+        CC1120_XOSC4,                          0xa0,       /* Crystal Oscillator Configuration, Reg 4 */\r
+        CC1120_XOSC3,                          0x03,       /* Crystal Oscillator Configuration, Reg 3 */\r
+        CC1120_XOSC2,                          0x04,       /* Crystal Oscillator Configuration, Reg 2 */\r
+        CC1120_XOSC1,                          0x01,       /* Crystal Oscillator Configuration, Reg 1 */\r
+        CC1120_XOSC0,                          0x00,       /* Crystal Oscillator Configuration, Reg 0 */\r
+        CC1120_ANALOG_SPARE,                   0x00,       /*  */\r
+        CC1120_PA_CFG3,                        0x00,       /* Power Amplifier Configuration, Reg 3 */\r
+        CC1120_WOR_TIME1,                      0x00,       /* eWOR Timer Status (MSB) */\r
+        CC1120_WOR_TIME0,                      0x00,       /* eWOR Timer Status (LSB) */\r
+        CC1120_WOR_CAPTURE1,                   0x00,       /* eWOR Timer Capture (MSB) */\r
+        CC1120_WOR_CAPTURE0,                   0x00,       /* eWOR Timer Capture (LSB) */\r
+        CC1120_BIST,                           0x00,       /* MARC BIST */\r
+        CC1120_DCFILTOFFSET_I1,                0x00,       /* DC Filter Offset I (MSB) */\r
+        CC1120_DCFILTOFFSET_I0,                0x00,       /* DC Filter Offset I (LSB) */\r
+        CC1120_DCFILTOFFSET_Q1,                0x00,       /* DC Filter Offset Q (MSB) */\r
+        CC1120_DCFILTOFFSET_Q0,                0x00,       /* DC Filter Offset Q (LSB) */\r
+        CC1120_IQIE_I1,                        0x00,       /* IQ Imbalance Value I (MSB) */\r
+        CC1120_IQIE_I0,                        0x00,       /* IQ Imbalance Value I (LSB) */\r
+        CC1120_IQIE_Q1,                        0x00,       /* IQ Imbalance Value Q (MSB) */\r
+        CC1120_IQIE_Q0,                        0x00,       /* IQ Imbalance Value Q (LSB) */\r
+        CC1120_RSSI1,                          0x80,       /* Received Signal Strength Indicator (MSB) */\r
+        CC1120_RSSI0,                          0x00,       /* Received Signal Strength Indicator (LSB) */\r
+        CC1120_MARCSTATE,                      0x41,       /* MARC State */\r
+        CC1120_LQI_VAL,                        0x00,       /* Link Quality Indicator Value */\r
+        CC1120_PQT_SYNC_ERR,                   0xff,       /* Preamble and Sync Word Error */\r
+        CC1120_DEM_STATUS,                     0x00,       /* Demodulator Status */\r
+        CC1120_FREQOFF_EST1,                   0x00,       /* Frequency Offset Estimate (MSB) */\r
+        CC1120_FREQOFF_EST0,                   0x00,       /* Frequency Offset Estimate (LSB) */\r
+        CC1120_AGC_GAIN3,                      0x00,       /* AGC Gain, Reg 3 */\r
+        CC1120_AGC_GAIN2,                      0xd1,       /* AGC Gain, Reg 2 */\r
+        CC1120_AGC_GAIN1,                      0x00,       /* AGC Gain, Reg 1 */\r
+        CC1120_AGC_GAIN0,                      0x3f,       /* AGC Gain, Reg 0 */\r
+        CC1120_SOFT_RX_DATA_OUT,               0x00,       /* Soft Decision Symbol Data */\r
+        CC1120_SOFT_TX_DATA_IN,                0x00,       /* Soft TX Data Input Register */\r
+        CC1120_ASK_SOFT_RX_DATA,               0x30,       /* AGC ASK Soft Decision Output */\r
+        CC1120_RNDGEN,                         0x7f,       /* Random Number Value */\r
+        CC1120_MAGN2,                          0x00,       /* Signal Magnitude after CORDIC [16] */\r
+        CC1120_MAGN1,                          0x00,       /* Signal Magnitude after CORDIC [15:8] */\r
+        CC1120_MAGN0,                          0x00,       /* Signal Magnitude after CORDIC [7:0] */\r
+        CC1120_ANG1,                           0x00,       /* Signal Angular after CORDIC [9:8] */\r
+        CC1120_ANG0,                           0x00,       /* Signal Angular after CORDIC [7:0] */\r
+        CC1120_CHFILT_I2,                      0x08,       /* Channel Filter Data Real Part [18:16] */\r
+        CC1120_CHFILT_I1,                      0x00,       /* Channel Filter Data Real Part [15:8] */\r
+        CC1120_CHFILT_I0,                      0x00,       /* Channel Filter Data Real Part [7:0] */\r
+        CC1120_CHFILT_Q2,                      0x00,       /* Channel Filter Data Imaginary Part [18:16] */\r
+        CC1120_CHFILT_Q1,                      0x00,       /* Channel Filter Data Imaginary Part [15:8] */\r
+        CC1120_CHFILT_Q0,                      0x00,       /* Channel Filter Data Imaginary Part [7:0] */\r
+        CC1120_GPIO_STATUS,                    0x00,       /* GPIO Status */\r
+        CC1120_FSCAL_CTRL,                     0x01,       /*  */\r
+        CC1120_PHASE_ADJUST,                   0x00,       /*  */\r
+        CC1120_PARTNUMBER,                     0x00,       /* Part Number */\r
+        CC1120_PARTVERSION,                    0x00,       /* Part Revision */\r
+        CC1120_SERIAL_STATUS,                  0x00,       /* Serial Status */\r
+        CC1120_RX_STATUS,                      0x01,       /* RX Status */\r
+        CC1120_TX_STATUS,                      0x00,       /* TX Status */\r
+        CC1120_MARC_STATUS1,                   0x00,       /* MARC Status, Reg 1 */\r
+        CC1120_MARC_STATUS0,                   0x00,       /* MARC Status, Reg 0 */\r
+        CC1120_PA_IFAMP_TEST,                  0x00,       /*  */\r
+        CC1120_FSRF_TEST,                      0x00,       /*  */\r
+        CC1120_PRE_TEST,                       0x00,       /*  */\r
+        CC1120_PRE_OVR,                        0x00,       /*  */\r
+        CC1120_ADC_TEST,                       0x00,       /* ADC Test */\r
+        CC1120_DVC_TEST,                       0x0b,       /* DVC Test */\r
+        CC1120_ATEST,                          0x40,       /*  */\r
+        CC1120_ATEST_LVDS,                     0x00,       /*  */\r
+        CC1120_ATEST_MODE,                     0x00,       /*  */\r
+        CC1120_XOSC_TEST1,                     0x3c,       /*  */\r
+        CC1120_XOSC_TEST0,                     0x00,       /*  */\r
+        CC1120_RXFIRST,                        0x00,       /* RX FIFO Pointer (first entry) */\r
+        CC1120_TXFIRST,                        0x00,       /* TX FIFO Pointer (first entry) */\r
+        CC1120_RXLAST,                         0x00,       /* RX FIFO Pointer (last entry) */\r
+        CC1120_TXLAST,                         0x00,       /* TX FIFO Pointer (last entry) */\r
+\r
diff --git a/src/drivers/ao_companion.c b/src/drivers/ao_companion.c
new file mode 100644 (file)
index 0000000..c749ade
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_companion.h>
+
+#ifdef MEGAMETRUM
+#define ao_spi_slow(b)
+#define ao_spi_fast(b)
+#endif
+
+#define COMPANION_SELECT()     do {                    \
+               ao_spi_get_bit(AO_COMPANION_CS_PORT,    \
+                              AO_COMPANION_CS_PIN,     \
+                              AO_COMPANION_CS,         \
+                              AO_COMPANION_SPI_BUS,    \
+                              AO_SPI_SPEED_200kHz);    \
+       } while (0)
+
+#define COMPANION_DESELECT()   do {                    \
+               ao_spi_put_bit(AO_COMPANION_CS_PORT,    \
+                              AO_COMPANION_CS_PIN,     \
+                              AO_COMPANION_CS,         \
+                              AO_COMPANION_SPI_BUS);   \
+       } while (0)
+
+__xdata struct ao_companion_command            ao_companion_command;
+__xdata struct ao_companion_setup              ao_companion_setup;
+
+__xdata uint16_t       ao_companion_data[AO_COMPANION_MAX_CHANNELS];
+__pdata uint8_t                ao_companion_running;
+__xdata uint8_t                ao_companion_mutex;
+
+static void
+ao_companion_send_command(uint8_t command)
+{
+       ao_companion_command.command = command;
+       ao_companion_command.flight_state = ao_flight_state;
+       ao_companion_command.tick = ao_time();
+       ao_companion_command.serial = ao_serial_number;
+       ao_companion_command.flight = ao_flight_number;
+       ao_companion_command.accel = ao_accel;
+       ao_companion_command.speed = ao_speed;
+       ao_companion_command.height = ao_height;
+       ao_companion_command.motor_number = ao_motor_number;
+       ao_spi_send(&ao_companion_command, sizeof (ao_companion_command), AO_COMPANION_SPI_BUS);
+}
+
+static uint8_t
+ao_companion_get_setup(void)
+{
+       COMPANION_SELECT();
+       ao_companion_send_command(AO_COMPANION_SETUP);
+       ao_spi_recv(&ao_companion_setup, sizeof (ao_companion_setup), AO_COMPANION_SPI_BUS);
+       COMPANION_DESELECT();
+       return (ao_companion_setup.board_id ==
+               (uint16_t) ~ao_companion_setup.board_id_inverse);
+}
+
+static void
+ao_companion_get_data(void)
+{
+       COMPANION_SELECT();
+       ao_companion_send_command(AO_COMPANION_FETCH);
+       ao_mutex_get(&ao_companion_mutex);
+       ao_spi_recv(&ao_companion_data, ao_companion_setup.channels * 2, AO_COMPANION_SPI_BUS);
+       ao_mutex_put(&ao_companion_mutex);
+       COMPANION_DESELECT();
+}
+
+static void
+ao_companion_notify(void)
+{
+       COMPANION_SELECT();
+       ao_companion_send_command(AO_COMPANION_NOTIFY);
+       COMPANION_DESELECT();
+}
+
+void
+ao_companion(void)
+{
+       uint8_t i;
+       while (!ao_flight_number)
+               ao_sleep(&ao_flight_number);
+       for (i = 0; i < 10; i++) {
+               ao_delay(AO_SEC_TO_TICKS(1));
+               if ((ao_companion_running = ao_companion_get_setup()))
+                   break;
+       }
+       while (ao_companion_running) {
+               ao_alarm(ao_companion_setup.update_period);
+               if (ao_sleep(DATA_TO_XDATA(&ao_flight_state)))
+                       ao_companion_get_data();
+               else
+                       ao_companion_notify();
+       }
+       ao_exit();
+}
+
+void
+ao_companion_status(void) __reentrant
+{
+       uint8_t i;
+       printf("Companion running: %d\n", ao_companion_running);
+       if (!ao_companion_running)
+               return;
+       printf("device: %d\n", ao_companion_setup.board_id);
+       printf("update period: %d\n", ao_companion_setup.update_period);
+       printf("channels: %d\n", ao_companion_setup.channels);
+       printf("data:");
+       for(i = 0; i < ao_companion_setup.channels; i++)
+               printf(" %5u", ao_companion_data[i]);
+       printf("\n");
+}
+
+__code struct ao_cmds ao_companion_cmds[] = {
+       { ao_companion_status,  "L\0Companion link status" },
+       { 0, NULL },
+};
+
+static __xdata struct ao_task ao_companion_task;
+
+void
+ao_companion_init(void)
+{
+       ao_enable_output(AO_COMPANION_CS_PORT, AO_COMPANION_CS_PIN, AO_COMPANION_CS, 1);
+       ao_cmd_register(&ao_companion_cmds[0]);
+       ao_add_task(&ao_companion_task, ao_companion, "companion");
+}
diff --git a/src/drivers/ao_event.c b/src/drivers/ao_event.c
new file mode 100644 (file)
index 0000000..440ef2d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_event.h>
+
+#define AO_EVENT_QUEUE 64
+
+#define ao_event_queue_next(n) (((n) + 1) & (AO_EVENT_QUEUE - 1))
+#define ao_event_queue_prev(n) (((n) - 1) & (AO_EVENT_QUEUE - 1))
+#define ao_event_queue_empty() (ao_event_queue_insert == ao_event_queue_remove)
+#define ao_event_queue_full()  (ao_event_queue_next(ao_event_queue_insert) == ao_event_queue_remove)
+
+/*
+ * Whether a sequence of events from the same device should be collapsed
+ */
+#define ao_event_can_collapse(type)    ((type) == AO_EVENT_QUADRATURE)
+
+struct ao_event ao_event_queue[AO_EVENT_QUEUE];
+uint8_t                ao_event_queue_insert;
+uint8_t                ao_event_queue_remove;
+
+
+uint8_t
+ao_event_get(struct ao_event *ev)
+{
+       ao_arch_critical(
+               while (ao_event_queue_empty())
+                       ao_sleep(&ao_event_queue);
+               *ev = ao_event_queue[ao_event_queue_remove];
+               ao_event_queue_remove = ao_event_queue_next(ao_event_queue_remove);
+               );
+}
+
+/* called with interrupts disabled */
+void
+ao_event_put_isr(uint8_t type, uint8_t unit, uint32_t value)
+{
+       if (!ao_event_queue_full()) {
+
+               if (ao_event_can_collapse(type) && !ao_event_queue_empty()) {
+                       uint8_t prev = ao_event_queue_prev(ao_event_queue_insert);
+
+                       if (ao_event_queue[prev].type == type &&
+                           ao_event_queue[prev].unit == unit)
+                               ao_event_queue_insert = prev;
+               }
+               ao_event_queue[ao_event_queue_insert] = (struct ao_event) {
+                       .type = type,
+                       .unit = unit,
+                       .tick = ao_tick_count,
+                       .value = value
+               };
+               ao_event_queue_insert = ao_event_queue_next(ao_event_queue_insert);
+               ao_wakeup(&ao_event_queue);
+       }
+}
+
+void
+ao_event_put(uint8_t type, uint8_t unit, uint32_t value)
+{
+       ao_arch_critical(ao_event_put_isr(type, unit, value););
+}
diff --git a/src/drivers/ao_event.h b/src/drivers/ao_event.h
new file mode 100644 (file)
index 0000000..25c49c3
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_EVENT_H_
+#define _AO_EVENT_H_
+
+#define AO_EVENT_NONE          0
+#define AO_EVENT_QUADRATURE    1
+#define AO_EVENT_BUTTON                2
+
+struct ao_event {
+       uint8_t         type;
+       uint8_t         unit;
+       uint16_t        tick;
+       uint32_t        value;
+};
+
+uint8_t
+ao_event_get(struct ao_event *ev);
+
+void
+ao_event_put_isr(uint8_t type, uint8_t unit, uint32_t value);
+
+void
+ao_event_put(uint8_t type, uint8_t unit, uint32_t value);
+
+#endif /* _AO_EVENT_H_ */
diff --git a/src/drivers/ao_gps_sirf.c b/src/drivers/ao_gps_sirf.c
new file mode 100644 (file)
index 0000000..91fc948
--- /dev/null
@@ -0,0 +1,448 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_GPS_TEST
+#include "ao.h"
+#endif
+
+__xdata uint8_t ao_gps_mutex;
+__pdata uint16_t ao_gps_tick;
+__xdata struct ao_telemetry_location   ao_gps_data;
+__xdata struct ao_telemetry_satellite  ao_gps_tracking_data;
+
+static const char ao_gps_set_nmea[] = "\r\n$PSRF100,0,57600,8,1,0*37\r\n";
+
+const char ao_gps_config[] = {
+
+       0xa0, 0xa2, 0x00, 0x0e, /* length: 14 bytes */
+       136,                    /* mode control */
+       0, 0,                   /* reserved */
+       0,                      /* degraded mode (allow 1-SV navigation) */
+       0, 0,                   /* reserved */
+       0, 0,                   /* user specified altitude */
+       2,                      /* alt hold mode (disabled, require 3d fixes) */
+       0,                      /* alt hold source (use last computed altitude) */
+       0,                      /* reserved */
+       10,                     /* Degraded time out (10 sec) */
+       10,                     /* Dead Reckoning time out (10 sec) */
+       0,                      /* Track smoothing (disabled) */
+       0x00, 0x8e, 0xb0, 0xb3,
+
+       0xa0, 0xa2, 0x00, 0x08, /* length: 8 bytes */
+       166,                    /* Set message rate */
+       2,                      /* enable/disable all messages */
+       0,                      /* message id (ignored) */
+       0,                      /* update rate (0 = disable) */
+       0, 0, 0, 0,             /* reserved */
+       0x00, 0xa8, 0xb0, 0xb3,
+
+       0xa0, 0xa2, 0x00, 0x02, /* length: 2 bytes */
+       143,                    /* static navigation */
+       0,                      /* disable */
+       0x00, 0x8f, 0xb0, 0xb3,
+};
+
+#define NAV_TYPE_GPS_FIX_TYPE_MASK                     (7 << 0)
+#define NAV_TYPE_NO_FIX                                        (0 << 0)
+#define NAV_TYPE_SV_KF                                 (1 << 0)
+#define NAV_TYPE_2_SV_KF                               (2 << 0)
+#define NAV_TYPE_3_SV_KF                               (3 << 0)
+#define NAV_TYPE_4_SV_KF                               (4 << 0)
+#define NAV_TYPE_2D_LEAST_SQUARES                      (5 << 0)
+#define NAV_TYPE_3D_LEAST_SQUARES                      (6 << 0)
+#define NAV_TYPE_DR                                    (7 << 0)
+#define NAV_TYPE_TRICKLE_POWER                         (1 << 3)
+#define NAV_TYPE_ALTITUDE_HOLD_MASK                    (3 << 4)
+#define NAV_TYPE_ALTITUDE_HOLD_NONE                    (0 << 4)
+#define NAV_TYPE_ALTITUDE_HOLD_KF                      (1 << 4)
+#define NAV_TYPE_ALTITUDE_HOLD_USER                    (2 << 4)
+#define NAV_TYPE_ALTITUDE_HOLD_ALWAYS                  (3 << 4)
+#define NAV_TYPE_DOP_LIMIT_EXCEEDED                    (1 << 6)
+#define NAV_TYPE_DGPS_APPLIED                          (1 << 7)
+#define NAV_TYPE_SENSOR_DR                             (1 << 8)
+#define NAV_TYPE_OVERDETERMINED                                (1 << 9)
+#define NAV_TYPE_DR_TIMEOUT_EXCEEDED                   (1 << 10)
+#define NAV_TYPE_FIX_MI_EDIT                           (1 << 11)
+#define NAV_TYPE_INVALID_VELOCITY                      (1 << 12)
+#define NAV_TYPE_ALTITUDE_HOLD_DISABLED                        (1 << 13)
+#define NAV_TYPE_DR_ERROR_STATUS_MASK                  (3 << 14)
+#define NAV_TYPE_DR_ERROR_STATUS_GPS_ONLY              (0 << 14)
+#define NAV_TYPE_DR_ERROR_STATUS_DR_FROM_GPS           (1 << 14)
+#define NAV_TYPE_DR_ERROR_STATUS_DR_SENSOR_ERROR       (2 << 14)
+#define NAV_TYPE_DR_ERROR_STATUS_DR_IN_TEST            (3 << 14)
+
+struct sirf_geodetic_nav_data {
+       uint16_t        nav_type;
+       uint16_t        utc_year;
+       uint8_t         utc_month;
+       uint8_t         utc_day;
+       uint8_t         utc_hour;
+       uint8_t         utc_minute;
+       uint16_t        utc_second;
+       int32_t         lat;
+       int32_t         lon;
+       int32_t         alt_msl;
+       uint16_t        ground_speed;
+       uint16_t        course;
+       int16_t         climb_rate;
+       uint32_t        h_error;
+       uint32_t        v_error;
+       uint8_t         num_sv;
+       uint8_t         hdop;
+};
+
+static __xdata struct sirf_geodetic_nav_data   ao_sirf_data;
+
+struct sirf_measured_sat_data {
+       uint8_t         svid;
+       uint8_t         c_n_1;
+};
+
+struct sirf_measured_tracker_data {
+       int16_t                         gps_week;
+       uint32_t                        gps_tow;
+       uint8_t                         channels;
+       struct sirf_measured_sat_data   sats[12];
+};
+
+static __xdata struct sirf_measured_tracker_data       ao_sirf_tracker_data;
+
+static __pdata uint16_t ao_sirf_cksum;
+static __pdata uint16_t ao_sirf_len;
+
+#ifndef ao_sirf_getchar
+#define ao_sirf_getchar                ao_serial1_getchar
+#define ao_sirf_putchar                ao_serial1_putchar
+#define ao_sirf_set_speed      ao_serial1_set_speed
+#endif
+
+#define ao_sirf_byte() ((uint8_t) ao_sirf_getchar())
+
+static uint8_t data_byte(void)
+{
+       uint8_t c = ao_sirf_byte();
+       --ao_sirf_len;
+       ao_sirf_cksum += c;
+       return c;
+}
+
+static char __xdata *sirf_target;
+
+static void sirf_u16(uint8_t offset)
+{
+       uint16_t __xdata *ptr = (uint16_t __xdata *) (sirf_target + offset);
+       uint16_t val;
+
+       val = data_byte() << 8;
+       val |= data_byte ();
+       *ptr = val;
+}
+
+static void sirf_u8(uint8_t offset)
+{
+       uint8_t __xdata *ptr = (uint8_t __xdata *) (sirf_target + offset);
+       uint8_t val;
+
+       val = data_byte ();
+       *ptr = val;
+}
+
+static void sirf_u32(uint8_t offset) __reentrant
+{
+       uint32_t __xdata *ptr = (uint32_t __xdata *) (sirf_target + offset);
+       uint32_t val;
+
+       val = ((uint32_t) data_byte ()) << 24;
+       val |= ((uint32_t) data_byte ()) << 16;
+       val |= ((uint32_t) data_byte ()) << 8;
+       val |= ((uint32_t) data_byte ());
+       *ptr = val;
+}
+
+static void sirf_discard(uint8_t len)
+{
+       while (len--)
+               data_byte();
+}
+
+#define SIRF_END       0
+#define SIRF_DISCARD   1
+#define SIRF_U8                2
+#define SIRF_U16       3
+#define SIRF_U32       4
+#define SIRF_U8X10     5
+
+struct sirf_packet_parse {
+       uint8_t type;
+       uint8_t offset;
+};
+
+static void
+ao_sirf_parse(void __xdata *target, const struct sirf_packet_parse *parse) __reentrant
+{
+       uint8_t i, offset, j;
+
+       sirf_target = target;
+       for (i = 0; ; i++) {
+               offset = parse[i].offset;
+               switch (parse[i].type) {
+               case SIRF_END:
+                       return;
+               case SIRF_DISCARD:
+                       sirf_discard(offset);
+                       break;
+               case SIRF_U8:
+                       sirf_u8(offset);
+                       break;
+               case SIRF_U16:
+                       sirf_u16(offset);
+                       break;
+               case SIRF_U32:
+                       sirf_u32(offset);
+                       break;
+               case SIRF_U8X10:
+                       for (j = 10; j--;)
+                               sirf_u8(offset++);
+                       break;
+               }
+       }
+}
+
+static const struct sirf_packet_parse geodetic_nav_data_packet[] = {
+       { SIRF_DISCARD, 2 },                                                    /* 1 nav valid */
+       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, nav_type) },        /* 3 */
+       { SIRF_DISCARD, 6 },                                                    /* 5 week number, time of week */
+       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, utc_year) },        /* 11 */
+       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_month) },        /* 13 */
+       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_day) },          /* 14 */
+       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_hour) },         /* 15 */
+       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, utc_minute) },       /* 16 */
+       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, utc_second) },      /* 17 */
+       { SIRF_DISCARD, 4 },    /* satellite id list */                         /* 19 */
+       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, lat) },             /* 23 */
+       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, lon) },             /* 27 */
+       { SIRF_DISCARD, 4 },    /* altitude from ellipsoid */                   /* 31 */
+       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, alt_msl) },         /* 35 */
+       { SIRF_DISCARD, 1 },    /* map datum */                                 /* 39 */
+       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, ground_speed) },    /* 40 */
+       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, course) },          /* 42 */
+       { SIRF_DISCARD, 2 },    /* magnetic variation */                        /* 44 */
+       { SIRF_U16, offsetof(struct sirf_geodetic_nav_data, climb_rate) },      /* 46 */
+       { SIRF_DISCARD, 2 },    /* turn rate */                                 /* 48 */
+       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, h_error) },         /* 50 */
+       { SIRF_U32, offsetof(struct sirf_geodetic_nav_data, v_error) },         /* 54 */
+       { SIRF_DISCARD, 30 },   /* time error, h_vel error, clock_bias,
+                                  clock bias error, clock drift,
+                                  clock drift error, distance,
+                                  distance error, heading error */             /* 58 */
+       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, num_sv) },           /* 88 */
+       { SIRF_U8, offsetof(struct sirf_geodetic_nav_data, hdop) },             /* 89 */
+       { SIRF_DISCARD, 1 },    /* additional mode info */                      /* 90 */
+       { SIRF_END, 0 },                                                        /* 91 */
+};
+
+static void
+ao_sirf_parse_41(void) __reentrant
+{
+       ao_sirf_parse(&ao_sirf_data, geodetic_nav_data_packet);
+}
+
+static const struct sirf_packet_parse measured_tracker_data_packet[] = {
+       { SIRF_U16, offsetof (struct sirf_measured_tracker_data, gps_week) },   /* 1 week */
+       { SIRF_U32, offsetof (struct sirf_measured_tracker_data, gps_tow) },    /* 3 time of week */
+       { SIRF_U8, offsetof (struct sirf_measured_tracker_data, channels) },    /* 7 channels */
+       { SIRF_END, 0 },
+};
+
+static const struct sirf_packet_parse measured_sat_data_packet[] = {
+       { SIRF_U8, offsetof (struct sirf_measured_sat_data, svid) },            /* 0 SV id */
+       { SIRF_DISCARD, 4 },                                                    /* 1 azimuth, 2 elevation, 3 state */
+       { SIRF_U8, offsetof (struct sirf_measured_sat_data, c_n_1) },           /* C/N0 1 */
+       { SIRF_DISCARD, 9 },                                                    /* C/N0 2-10 */
+       { SIRF_END, 0 },
+};
+
+static void
+ao_sirf_parse_4(void) __reentrant
+{
+       uint8_t i;
+       ao_sirf_parse(&ao_sirf_tracker_data, measured_tracker_data_packet);
+       for (i = 0; i < 12; i++)
+               ao_sirf_parse(&ao_sirf_tracker_data.sats[i], measured_sat_data_packet);
+}
+
+static void
+ao_gps_setup(void) __reentrant
+{
+       uint8_t i, k;
+       ao_sirf_set_speed(AO_SERIAL_SPEED_4800);
+       for (i = 0; i < 64; i++)
+               ao_sirf_putchar(0x00);
+       for (k = 0; k < 3; k++)
+               for (i = 0; i < sizeof (ao_gps_set_nmea); i++)
+                       ao_sirf_putchar(ao_gps_set_nmea[i]);
+       ao_sirf_set_speed(AO_SERIAL_SPEED_57600);
+       for (i = 0; i < 64; i++)
+               ao_sirf_putchar(0x00);
+}
+
+static const char ao_gps_set_message_rate[] = {
+       0xa0, 0xa2, 0x00, 0x08,
+       166,
+       0,
+};
+
+void
+ao_sirf_set_message_rate(uint8_t msg, uint8_t rate) __reentrant
+{
+       uint16_t        cksum = 0x00a6;
+       uint8_t         i;
+
+       for (i = 0; i < sizeof (ao_gps_set_message_rate); i++)
+               ao_sirf_putchar(ao_gps_set_message_rate[i]);
+       ao_sirf_putchar(msg);
+       ao_sirf_putchar(rate);
+       cksum = 0xa6 + msg + rate;
+       for (i = 0; i < 4; i++)
+               ao_sirf_putchar(0);
+       ao_sirf_putchar((cksum >> 8) & 0x7f);
+       ao_sirf_putchar(cksum & 0xff);
+       ao_sirf_putchar(0xb0);
+       ao_sirf_putchar(0xb3);
+}
+
+static const uint8_t sirf_disable[] = {
+       2,
+       9,
+       10,
+       27,
+       50,
+       52,
+};
+
+void
+ao_gps(void) __reentrant
+{
+       uint8_t i, k;
+       uint16_t cksum;
+
+       ao_gps_setup();
+       for (k = 0; k < 5; k++)
+       {
+               for (i = 0; i < sizeof (ao_gps_config); i++)
+                       ao_sirf_putchar(ao_gps_config[i]);
+               for (i = 0; i < sizeof (sirf_disable); i++)
+                       ao_sirf_set_message_rate(sirf_disable[i], 0);
+               ao_sirf_set_message_rate(41, 1);
+               ao_sirf_set_message_rate(4, 1);
+       }
+       for (;;) {
+               /* Locate the begining of the next record */
+               while (ao_sirf_byte() != (uint8_t) 0xa0)
+                       ;
+               if (ao_sirf_byte() != (uint8_t) 0xa2)
+                       continue;
+
+               /* Length */
+               ao_sirf_len = ao_sirf_byte() << 8;
+               ao_sirf_len |= ao_sirf_byte();
+               if (ao_sirf_len > 1023)
+                       continue;
+
+               ao_sirf_cksum = 0;
+
+               /* message ID */
+               i = data_byte ();                                                       /* 0 */
+
+               switch (i) {
+               case 41:
+                       if (ao_sirf_len < 90)
+                               break;
+                       ao_sirf_parse_41();
+                       break;
+               case 4:
+                       if (ao_sirf_len < 187)
+                               break;
+                       ao_sirf_parse_4();
+                       break;
+               }
+               if (ao_sirf_len != 0)
+                       continue;
+
+               /* verify checksum and end sequence */
+               ao_sirf_cksum &= 0x7fff;
+               cksum = ao_sirf_byte() << 8;
+               cksum |= ao_sirf_byte();
+               if (ao_sirf_cksum != cksum)
+                       continue;
+               if (ao_sirf_byte() != (uint8_t) 0xb0)
+                       continue;
+               if (ao_sirf_byte() != (uint8_t) 0xb3)
+                       continue;
+
+               switch (i) {
+               case 41:
+                       ao_mutex_get(&ao_gps_mutex);
+                       ao_gps_tick = ao_time();
+                       ao_gps_data.hour = ao_sirf_data.utc_hour;
+                       ao_gps_data.minute = ao_sirf_data.utc_minute;
+                       ao_gps_data.second = ao_sirf_data.utc_second / 1000;
+                       ao_gps_data.flags = ((ao_sirf_data.num_sv << AO_GPS_NUM_SAT_SHIFT) & AO_GPS_NUM_SAT_MASK) | AO_GPS_RUNNING;
+                       if ((ao_sirf_data.nav_type & NAV_TYPE_GPS_FIX_TYPE_MASK) >= NAV_TYPE_4_SV_KF)
+                               ao_gps_data.flags |= AO_GPS_VALID;
+                       ao_gps_data.latitude = ao_sirf_data.lat;
+                       ao_gps_data.longitude = ao_sirf_data.lon;
+                       ao_gps_data.altitude = ao_sirf_data.alt_msl / 100;
+                       ao_gps_data.ground_speed = ao_sirf_data.ground_speed;
+                       ao_gps_data.course = ao_sirf_data.course / 200;
+                       ao_gps_data.hdop = ao_sirf_data.hdop;
+                       ao_gps_data.climb_rate = ao_sirf_data.climb_rate;
+                       ao_gps_data.flags |= AO_GPS_COURSE_VALID;
+#if 0
+                       if (ao_sirf_data.h_error > 6553500)
+                               ao_gps_data.h_error = 65535;
+                       else
+                               ao_gps_data.h_error = ao_sirf_data.h_error / 100;
+                       if (ao_sirf_data.v_error > 6553500)
+                               ao_gps_data.v_error = 65535;
+                       else
+                               ao_gps_data.v_error = ao_sirf_data.v_error / 100;
+#endif
+                       ao_mutex_put(&ao_gps_mutex);
+                       ao_wakeup(&ao_gps_data);
+                       break;
+               case 4:
+                       ao_mutex_get(&ao_gps_mutex);
+                       ao_gps_tracking_data.channels = ao_sirf_tracker_data.channels;
+                       for (i = 0; i < 12; i++) {
+                               ao_gps_tracking_data.sats[i].svid = ao_sirf_tracker_data.sats[i].svid;
+                               ao_gps_tracking_data.sats[i].c_n_1 = ao_sirf_tracker_data.sats[i].c_n_1;
+                       }
+                       ao_mutex_put(&ao_gps_mutex);
+                       ao_wakeup(&ao_gps_tracking_data);
+                       break;
+               }
+       }
+}
+
+__xdata struct ao_task ao_gps_task;
+
+void
+ao_gps_init(void)
+{
+       ao_add_task(&ao_gps_task, ao_gps, "gps");
+}
diff --git a/src/drivers/ao_gps_skytraq.c b/src/drivers/ao_gps_skytraq.c
new file mode 100644 (file)
index 0000000..d80da97
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_GPS_TEST
+#include "ao.h"
+#endif
+
+#ifndef ao_gps_getchar
+#define ao_gps_getchar ao_serial1_getchar
+#endif
+
+#ifndef ao_gps_putchar
+#define ao_gps_putchar ao_serial1_putchar
+#endif
+
+#ifndef ao_gps_set_speed
+#define ao_gps_set_speed       ao_serial1_set_speed
+#endif
+
+__xdata uint8_t ao_gps_mutex;
+static __data char ao_gps_char;
+static __data uint8_t ao_gps_cksum;
+static __data uint8_t ao_gps_error;
+
+__pdata uint16_t ao_gps_tick;
+__xdata struct ao_telemetry_location   ao_gps_data;
+__xdata struct ao_telemetry_satellite  ao_gps_tracking_data;
+
+static __pdata uint16_t                                ao_gps_next_tick;
+static __pdata struct ao_telemetry_location    ao_gps_next;
+static __pdata uint8_t                         ao_gps_date_flags;
+static __pdata struct ao_telemetry_satellite   ao_gps_tracking_next;
+
+#define STQ_S 0xa0, 0xa1
+#define STQ_E 0x0d, 0x0a
+#define SKYTRAQ_MSG_2(id,a,b) \
+    STQ_S, 0, 3, id, a,b, (id^a^b), STQ_E
+#define SKYTRAQ_MSG_3(id,a,b,c) \
+    STQ_S, 0, 4, id, a,b,c, (id^a^b^c), STQ_E
+#define SKYTRAQ_MSG_8(id,a,b,c,d,e,f,g,h) \
+    STQ_S, 0, 9, id, a,b,c,d,e,f,g,h, (id^a^b^c^d^e^f^g^h), STQ_E
+#define SKYTRAQ_MSG_14(id,a,b,c,d,e,f,g,h,i,j,k,l,m,n) \
+    STQ_S, 0,15, id, a,b,c,d,e,f,g,h,i,j,k,l,m,n, \
+    (id^a^b^c^d^e^f^g^h^i^j^k^l^m^n), STQ_E
+
+static __code uint8_t ao_gps_config[] = {
+       SKYTRAQ_MSG_8(0x08, 1, 0, 1, 0, 1, 0, 0, 0), /* configure nmea */
+       /* gga interval */
+       /* gsa interval */
+       /* gsv interval */
+       /* gll interval */
+       /* rmc interval */
+       /* vtg interval */
+       /* zda interval */
+       /* attributes (0 = update to sram, 1 = update flash too) */
+
+       SKYTRAQ_MSG_2(0x3c, 0x00, 0x00), /* configure navigation mode */
+       /* 0 = car, 1 = pedestrian */
+       /* 0 = update to sram, 1 = update sram + flash */
+};
+
+static void
+ao_gps_lexchar(void)
+{
+       char c;
+       if (ao_gps_error)
+               c = '\n';
+       else
+               c = ao_gps_getchar();
+       ao_gps_cksum ^= c;
+       ao_gps_char = c;
+}
+
+void
+ao_gps_skip_field(void)
+{
+       for (;;) {
+               char c = ao_gps_char;
+               if (c == ',' || c == '*' || c == '\n')
+                       break;
+               ao_gps_lexchar();
+       }
+}
+
+void
+ao_gps_skip_sep(void)
+{
+       char c = ao_gps_char;
+       if (c == ',' || c == '.' || c == '*')
+               ao_gps_lexchar();
+}
+
+__data static uint8_t ao_gps_num_width;
+
+static int16_t
+ao_gps_decimal(uint8_t max_width)
+{
+       int16_t v;
+       uint8_t neg = 0;
+
+       ao_gps_skip_sep();
+       if (ao_gps_char == '-') {
+               neg = 1;
+               ao_gps_lexchar();
+       }
+       v = 0;
+       ao_gps_num_width = 0;
+       while (ao_gps_num_width < max_width) {
+               uint8_t c = ao_gps_char;
+               if (c < (uint8_t) '0' || (uint8_t) '9' < c)
+                       break;
+               v = v * 10 + (uint8_t) (c - (uint8_t) '0');
+               ao_gps_num_width++;
+               ao_gps_lexchar();
+       }
+       if (neg)
+               v = -v;
+       return v;
+}
+
+static uint8_t
+ao_gps_hex(void)
+{
+       uint8_t v;
+
+       ao_gps_skip_sep();
+       v = 0;
+       ao_gps_num_width = 0;
+       while (ao_gps_num_width < 2) {
+               uint8_t c = ao_gps_char;
+               uint8_t d;
+               if ((uint8_t) '0' <= c && c <= (uint8_t) '9')
+                       d = - '0';
+               else if ((uint8_t) 'A' <= c && c <= (uint8_t) 'F')
+                       d = - 'A' + 10;
+               else if ((uint8_t) 'a' <= c && c <= (uint8_t) 'f')
+                       d = - 'a' + 10;
+               else
+                       break;
+               v = (v << 4) | (c + d);
+               ao_gps_num_width++;
+               ao_gps_lexchar();
+       }
+       return v;
+}
+
+static int32_t
+ao_gps_parse_pos(uint8_t deg_width) __reentrant
+{
+       static __pdata uint16_t d;
+       static __pdata uint8_t  m;
+       static __pdata uint16_t f;
+       char c;
+
+       d = ao_gps_decimal(deg_width);
+       m = ao_gps_decimal(2);
+       c = ao_gps_char;
+       if (c == '.') {
+               f = ao_gps_decimal(4);
+               while (ao_gps_num_width < 4) {
+                       f *= 10;
+                       ao_gps_num_width++;
+               }
+       } else {
+               f = 0;
+               if (c != ',')
+                       ao_gps_error = 1;
+       }
+       return d * 10000000l + (m * 10000l + f) * 50 / 3;
+}
+
+static uint8_t
+ao_gps_parse_flag(char no_c, char yes_c)
+{
+       uint8_t ret = 0;
+       ao_gps_skip_sep();
+       if (ao_gps_char == yes_c)
+               ret = 1;
+       else if (ao_gps_char == no_c)
+               ret = 0;
+       else
+               ao_gps_error = 1;
+       ao_gps_lexchar();
+       return ret;
+}
+
+static void
+ao_nmea_finish(void)
+{
+       char c;
+       /* Skip remaining fields */
+       for (;;) {
+               c = ao_gps_char;
+               if (c == '*' || c == '\n' || c == '\r')
+                       break;
+               ao_gps_lexchar();
+               ao_gps_skip_field();
+       }
+       if (c == '*') {
+               uint8_t cksum = ao_gps_cksum ^ '*';
+               if (cksum != ao_gps_hex())
+                       ao_gps_error = 1;
+       } else
+               ao_gps_error = 1;
+}
+
+static void
+ao_nmea_gga(void)
+{
+       uint8_t i;
+
+       /* Now read the data into the gps data record
+        *
+        * $GPGGA,025149.000,4528.1723,N,12244.2480,W,1,05,2.0,103.5,M,-19.5,M,,0000*66
+        *
+        * Essential fix data
+        *
+        *         025149.000   time (02:51:49.000 GMT)
+        *         4528.1723,N  Latitude 45°28.1723' N
+        *         12244.2480,W Longitude 122°44.2480' W
+        *         1            Fix quality:
+        *                                 0 = invalid
+        *                                 1 = GPS fix (SPS)
+        *                                 2 = DGPS fix
+        *                                 3 = PPS fix
+        *                                 4 = Real Time Kinematic
+        *                                 5 = Float RTK
+        *                                 6 = estimated (dead reckoning)
+        *                                 7 = Manual input mode
+        *                                 8 = Simulation mode
+        *         05           Number of satellites (5)
+        *         2.0          Horizontal dilution
+        *         103.5,M              Altitude, 103.5M above msl
+        *         -19.5,M              Height of geoid above WGS84 ellipsoid
+        *         ?            time in seconds since last DGPS update
+        *         0000         DGPS station ID
+        *         *66          checksum
+        */
+
+       ao_gps_next_tick = ao_time();
+       ao_gps_next.flags = AO_GPS_RUNNING | ao_gps_date_flags;
+       ao_gps_next.hour = ao_gps_decimal(2);
+       ao_gps_next.minute = ao_gps_decimal(2);
+       ao_gps_next.second = ao_gps_decimal(2);
+       ao_gps_skip_field();    /* skip seconds fraction */
+
+       ao_gps_next.latitude = ao_gps_parse_pos(2);
+       if (ao_gps_parse_flag('N', 'S'))
+               ao_gps_next.latitude = -ao_gps_next.latitude;
+       ao_gps_next.longitude = ao_gps_parse_pos(3);
+       if (ao_gps_parse_flag('E', 'W'))
+               ao_gps_next.longitude = -ao_gps_next.longitude;
+
+       i = ao_gps_decimal(0xff);
+       if (i == 1)
+               ao_gps_next.flags |= AO_GPS_VALID;
+
+       i = ao_gps_decimal(0xff) << AO_GPS_NUM_SAT_SHIFT;
+       if (i > AO_GPS_NUM_SAT_MASK)
+               i = AO_GPS_NUM_SAT_MASK;
+       ao_gps_next.flags |= i;
+
+       ao_gps_lexchar();
+       i = ao_gps_decimal(0xff);
+       if (i <= 50) {
+               i = (uint8_t) 5 * i;
+               if (ao_gps_char == '.')
+                       i = (i + ((uint8_t) ao_gps_decimal(1) >> 1));
+       } else
+               i = 255;
+       ao_gps_next.hdop = i;
+       ao_gps_skip_field();
+
+       ao_gps_next.altitude = ao_gps_decimal(0xff);
+       ao_gps_skip_field();    /* skip any fractional portion */
+
+       ao_nmea_finish();
+
+       if (!ao_gps_error) {
+               ao_mutex_get(&ao_gps_mutex);
+               ao_gps_tick = ao_gps_next_tick;
+               ao_xmemcpy(&ao_gps_data, PDATA_TO_XDATA(&ao_gps_next), sizeof (ao_gps_data));
+               ao_mutex_put(&ao_gps_mutex);
+               ao_wakeup(&ao_gps_data);
+       }
+}
+
+static void
+ao_nmea_gsv(void)
+{
+       char    c;
+       uint8_t i;
+       uint8_t done;
+       /* Now read the data into the GPS tracking data record
+        *
+        * $GPGSV,3,1,12,05,54,069,45,12,44,061,44,21,07,184,46,22,78,289,47*72<CR><LF>
+        *
+        * Satellites in view data
+        *
+        *      3               Total number of GSV messages
+        *      1               Sequence number of current GSV message
+        *      12              Total sats in view (0-12)
+        *      05              SVID
+        *      54              Elevation
+        *      069             Azimuth
+        *      45              C/N0 in dB
+        *      ...             other SVIDs
+        *      72              checksum
+        */
+       c = ao_gps_decimal(1);  /* total messages */
+       i = ao_gps_decimal(1);  /* message sequence */
+       if (i == 1) {
+               ao_gps_tracking_next.channels = 0;
+       }
+       done = (uint8_t) c == i;
+       ao_gps_lexchar();
+       ao_gps_skip_field();    /* sats in view */
+       while (ao_gps_char != '*' && ao_gps_char != '\n' && ao_gps_char != '\r') {
+               i = ao_gps_tracking_next.channels;
+               c = ao_gps_decimal(2);  /* SVID */
+               if (i < AO_MAX_GPS_TRACKING)
+                       ao_gps_tracking_next.sats[i].svid = c;
+               ao_gps_lexchar();
+               ao_gps_skip_field();    /* elevation */
+               ao_gps_lexchar();
+               ao_gps_skip_field();    /* azimuth */
+               c = ao_gps_decimal(2);  /* C/N0 */
+               if (i < AO_MAX_GPS_TRACKING) {
+                       if ((ao_gps_tracking_next.sats[i].c_n_1 = c) != 0)
+                               ao_gps_tracking_next.channels = i + 1;
+               }
+       }
+
+       ao_nmea_finish();
+
+       if (ao_gps_error)
+               ao_gps_tracking_next.channels = 0;
+       else if (done) {
+               ao_mutex_get(&ao_gps_mutex);
+               ao_xmemcpy(&ao_gps_tracking_data, PDATA_TO_XDATA(&ao_gps_tracking_next), sizeof(ao_gps_tracking_data));
+               ao_mutex_put(&ao_gps_mutex);
+               ao_wakeup(&ao_gps_tracking_data);
+       }
+}
+
+static void
+ao_nmea_rmc(void)
+{
+       char    a, c;
+       uint8_t i;
+       /* Parse the RMC record to read out the current date */
+
+       /* $GPRMC,111636.932,A,2447.0949,N,12100.5223,E,000.0,000.0,030407,,,A*61
+        *
+        * Recommended Minimum Specific GNSS Data
+        *
+        *      111636.932      UTC time 11:16:36.932
+        *      A               Data Valid (V = receiver warning)
+        *      2447.0949       Latitude
+        *      N               North/south indicator
+        *      12100.5223      Longitude
+        *      E               East/west indicator
+        *      000.0           Speed over ground
+        *      000.0           Course over ground
+        *      030407          UTC date (ddmmyy format)
+        *      A               Mode indicator:
+        *                      N = data not valid
+        *                      A = autonomous mode
+        *                      D = differential mode
+        *                      E = estimated (dead reckoning) mode
+        *                      M = manual input mode
+        *                      S = simulator mode
+        *      61              checksum
+        */
+       ao_gps_skip_field();
+       for (i = 0; i < 8; i++) {
+               ao_gps_lexchar();
+               ao_gps_skip_field();
+       }
+       a = ao_gps_decimal(2);
+       c = ao_gps_decimal(2);
+       i = ao_gps_decimal(2);
+
+       ao_nmea_finish();
+
+       if (!ao_gps_error) {
+               ao_gps_next.year = i;
+               ao_gps_next.month = c;
+               ao_gps_next.day = a;
+               ao_gps_date_flags = AO_GPS_DATE_VALID;
+       }
+}
+
+#define ao_skytraq_sendstruct(s) ao_skytraq_sendbytes((s), sizeof(s))
+
+static void
+ao_skytraq_sendbytes(__code uint8_t *b, uint8_t l)
+{
+       while (l--) {
+               uint8_t c = *b++;
+               if (c == 0xa0)
+                       ao_delay(AO_MS_TO_TICKS(500));
+               ao_gps_putchar(c);
+       }
+}
+
+static void
+ao_gps_nmea_parse(void)
+{
+       uint8_t a, b, c;
+
+       ao_gps_cksum = 0;
+       ao_gps_error = 0;
+
+       ao_gps_lexchar();
+       if (ao_gps_char != 'G')
+               return;
+       ao_gps_lexchar();
+       if (ao_gps_char != 'P')
+               return;
+
+       ao_gps_lexchar();
+       a = ao_gps_char;
+       ao_gps_lexchar();
+       b = ao_gps_char;
+       ao_gps_lexchar();
+       c = ao_gps_char;
+       ao_gps_lexchar();
+
+       if (ao_gps_char != ',')
+               return;
+
+       if (a == (uint8_t) 'G' && b == (uint8_t) 'G' && c == (uint8_t) 'A') {
+               ao_nmea_gga();
+       } else if (a == (uint8_t) 'G' && b == (uint8_t) 'S' && c == (uint8_t) 'V') {
+               ao_nmea_gsv();
+       } else if (a == (uint8_t) 'R' && b == (uint8_t) 'M' && c == (uint8_t) 'C') {
+               ao_nmea_rmc();
+       }
+}
+
+void
+ao_gps(void) __reentrant
+{
+       ao_gps_set_speed(AO_SERIAL_SPEED_9600);
+
+       /* give skytraq time to boot in case of cold start */
+       ao_delay(AO_MS_TO_TICKS(2000));
+
+       ao_skytraq_sendstruct(ao_gps_config);
+
+       for (;;) {
+               /* Locate the begining of the next record */
+               if (ao_gps_getchar() == '$') {
+                       ao_gps_nmea_parse();
+               }
+       }
+}
+
+__xdata struct ao_task ao_gps_task;
+
+static void
+gps_dump(void) __reentrant
+{
+       uint8_t i;
+       ao_mutex_get(&ao_gps_mutex);
+       printf ("Date: %02d/%02d/%02d\n", ao_gps_data.year, ao_gps_data.month, ao_gps_data.day);
+       printf ("Time: %02d:%02d:%02d\n", ao_gps_data.hour, ao_gps_data.minute, ao_gps_data.second);
+       printf ("Lat/Lon: %ld %ld\n", (long) ao_gps_data.latitude, (long) ao_gps_data.longitude);
+       printf ("Alt: %d\n", ao_gps_data.altitude);
+       printf ("Flags: 0x%x\n", ao_gps_data.flags);
+       printf ("Sats: %d", ao_gps_tracking_data.channels);
+       for (i = 0; i < ao_gps_tracking_data.channels; i++)
+               printf (" %d %d",
+                       ao_gps_tracking_data.sats[i].svid,
+                       ao_gps_tracking_data.sats[i].c_n_1);
+       printf ("\ndone\n");
+       ao_mutex_put(&ao_gps_mutex);
+}
+
+__code struct ao_cmds ao_gps_cmds[] = {
+       { gps_dump,     "g\0Display GPS" },
+       { 0, NULL },
+};
+
+void
+ao_gps_init(void)
+{
+       ao_add_task(&ao_gps_task, ao_gps, "gps");
+       ao_cmd_register(&ao_gps_cmds[0]);
+}
diff --git a/src/drivers/ao_hmc5883.c b/src/drivers/ao_hmc5883.c
new file mode 100644 (file)
index 0000000..ade6c26
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_hmc5883.h>
+#include <ao_exti.h>
+
+#if HAS_HMC5883
+
+static uint8_t ao_hmc5883_configured;
+
+static uint8_t ao_hmc5883_addr;
+
+static void
+ao_hmc5883_reg_write(uint8_t addr, uint8_t data)
+{
+       uint8_t d[2];
+
+       d[0] = addr;
+       d[1] = data;
+       ao_i2c_get(AO_HMC5883_I2C_INDEX);
+       ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_WRITE);
+       ao_i2c_send(d, 2, AO_HMC5883_I2C_INDEX, TRUE);
+       ao_i2c_put(AO_HMC5883_I2C_INDEX);
+       ao_hmc5883_addr = addr + 1;
+}
+
+static void
+ao_hmc5883_read(uint8_t addr, uint8_t *data, uint8_t len)
+{
+       ao_i2c_get(AO_HMC5883_I2C_INDEX);
+       if (addr != ao_hmc5883_addr) {
+               ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_WRITE);
+               ao_i2c_send(&addr, 1, AO_HMC5883_I2C_INDEX, FALSE);
+       }
+       ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_READ);
+       ao_i2c_recv(data, len, AO_HMC5883_I2C_INDEX, TRUE);
+       ao_i2c_put(AO_HMC5883_I2C_INDEX);
+       ao_hmc5883_addr = 0xff;
+}
+
+static uint8_t ao_hmc5883_done;
+
+static void
+ao_hmc5883_isr(void)
+{
+       ao_exti_disable(AO_HMC5883_INT_PORT, AO_HMC5883_INT_PIN);
+       ao_hmc5883_done = 1;
+       ao_wakeup(&ao_hmc5883_done);
+}
+
+static uint32_t        ao_hmc5883_missed_irq;
+
+void
+ao_hmc5883_sample(struct ao_hmc5883_sample *sample)
+{
+       uint16_t        *d = (uint16_t *) sample;
+       int             i = sizeof (*sample) / 2;
+       uint8_t         single = HMC5883_MODE_SINGLE;
+
+       ao_hmc5883_done = 0;
+       ao_exti_enable(AO_HMC5883_INT_PORT, AO_HMC5883_INT_PIN);
+       ao_hmc5883_reg_write(HMC5883_MODE, HMC5883_MODE_SINGLE);
+
+       ao_alarm(AO_MS_TO_TICKS(10));
+       cli();
+       while (!ao_hmc5883_done)
+               if (ao_sleep(&ao_hmc5883_done))
+                       ++ao_hmc5883_missed_irq;
+       sei();
+       ao_clear_alarm();
+
+       ao_hmc5883_read(HMC5883_X_MSB, (uint8_t *) sample, sizeof (struct ao_hmc5883_sample));
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       /* byte swap */
+       while (i--) {
+               uint16_t        t = *d;
+               *d++ = (t >> 8) | (t << 8);
+       }
+#endif
+}
+
+static uint8_t
+ao_hmc5883_setup(void)
+{
+       uint8_t d;
+       uint8_t present;
+
+       if (ao_hmc5883_configured)
+               return 1;
+
+       ao_i2c_get(AO_HMC5883_I2C_INDEX);
+       present = ao_i2c_start(AO_HMC5883_I2C_INDEX, HMC5883_ADDR_READ);
+       ao_i2c_recv(&d, 1, AO_HMC5883_I2C_INDEX, TRUE);
+       ao_i2c_put(AO_HMC5883_I2C_INDEX);
+
+       if (!present)
+               ao_panic(AO_PANIC_SELF_TEST);
+
+       ao_hmc5883_reg_write(HMC5883_CONFIG_A,
+                            (HMC5883_CONFIG_A_MA_8 << HMC5883_CONFIG_A_MA) |
+                            (HMC5883_CONFIG_A_DO_15 << HMC5883_CONFIG_A_DO) |
+                            (HMC5883_CONFIG_A_MS_NORMAL << HMC5883_CONFIG_A_MS));
+
+       ao_hmc5883_reg_write(HMC5883_CONFIG_B,
+                            (HMC5883_CONFIG_B_GN_1_3 << HMC5883_CONFIG_B_GN));
+
+       ao_hmc5883_configured = 1;
+       return 1;
+}
+
+static void
+ao_hmc5883(void)
+{
+       ao_hmc5883_setup();
+       for (;;) {
+               ao_hmc5883_sample((struct ao_hmc5883_sample *) &ao_data_ring[ao_data_head].hmc5883);
+               ao_arch_critical(
+                       AO_DATA_PRESENT(AO_DATA_HMC5883);
+                       AO_DATA_WAIT();
+                       );
+       }
+}
+
+static struct ao_task ao_hmc5883_task;
+
+static void
+ao_hmc5883_show(void)
+{
+       struct ao_data  sample;
+       ao_data_get(&sample);
+       printf ("X: %d Y: %d Z: %d missed irq: %lu\n",
+               sample.hmc5883.x, sample.hmc5883.y, sample.hmc5883.z, ao_hmc5883_missed_irq);
+}
+
+static const struct ao_cmds ao_hmc5883_cmds[] = {
+       { ao_hmc5883_show,      "M\0Show HMC5883 status" },
+       { 0, NULL }
+};
+
+void
+ao_hmc5883_init(void)
+{
+       ao_hmc5883_configured = 0;
+
+       ao_enable_port(AO_HMC5883_INT_PORT);
+       ao_exti_setup(AO_HMC5883_INT_PORT,
+                     AO_HMC5883_INT_PIN,
+                     AO_EXTI_MODE_FALLING | AO_EXTI_MODE_PULL_UP,
+                     ao_hmc5883_isr);
+
+       ao_add_task(&ao_hmc5883_task, ao_hmc5883, "hmc5883");
+       ao_cmd_register(&ao_hmc5883_cmds[0]);
+}
+
+#endif
diff --git a/src/drivers/ao_hmc5883.h b/src/drivers/ao_hmc5883.h
new file mode 100644 (file)
index 0000000..5569097
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_HMC5883_H_
+#define _AO_HMC5883_H_
+
+#define HMC5883_ADDR_WRITE     0x3c
+#define HMC5883_ADDR_READ      0x3d
+
+#define HMC5883_CONFIG_A       0
+
+#define  HMC5883_CONFIG_A_MA           5
+#define  HMC5883_CONFIG_A_MA_1                 0
+#define  HMC5883_CONFIG_A_MA_2                 1
+#define  HMC5883_CONFIG_A_MA_4                 2
+#define  HMC5883_CONFIG_A_MA_8                 3
+#define  HMC5883_CONFIG_A_MA_MASK              3
+
+#define  HMC5883_CONFIG_A_DO           2
+#define   HMC5883_CONFIG_A_DO_0_75             0
+#define   HMC5883_CONFIG_A_DO_1_5              1
+#define   HMC5883_CONFIG_A_DO_3                        2
+#define   HMC5883_CONFIG_A_DO_7_5              3
+#define   HMC5883_CONFIG_A_DO_15               4
+#define   HMC5883_CONFIG_A_DO_30               5
+#define   HMC5883_CONFIG_A_DO_75               6
+#define   HMC5883_CONFIG_A_DO_MASK             7
+
+#define HMC5883_CONFIG_A_MS            0
+#define  HMC5883_CONFIG_A_MS_NORMAL            0
+#define  HMC5883_CONFIG_A_MS_POSITIVE_BIAS     1
+#define  HMC5883_CONFIG_A_MS_NEGATIVE_BIAS     2
+#define  HMC5883_CONFIG_A_MS_MASK              3
+
+#define HMC5883_CONFIG_B       1
+
+#define HMC5883_CONFIG_B_GN            5
+#define  HMC5883_CONFIG_B_GN_0_88              0
+#define  HMC5883_CONFIG_B_GN_1_3               1
+#define  HMC5883_CONFIG_B_GN_1_9               2
+#define  HMC5883_CONFIG_B_GN_2_5               3
+#define  HMC5883_CONFIG_B_GN_4_0               4
+#define  HMC5883_CONFIG_B_GN_4_7               5
+#define  HMC5883_CONFIG_B_GN_5_6               6
+#define  HMC5883_CONFIG_B_GN_8_1               7
+#define  HMC5883_CONFIG_B_GN_MASK              7
+
+#define HMC5883_MODE           2
+#define  HMC5883_MODE_CONTINUOUS       0
+#define  HMC5883_MODE_SINGLE           1
+#define  HMC5883_MODE_IDLE             2
+
+#define HMC5883_X_MSB          3
+#define HMC5883_X_LSB          4
+#define HMC5883_Y_MSB          5
+#define HMC5883_Y_LSB          6
+#define HMC5883_Z_MSB          7
+#define HMC5883_Z_LSB          8
+#define HMC5883_STATUS         9
+#define HMC5883_ID_A           10
+#define HMC5883_ID_B           11
+#define HMC5883_ID_C           12
+
+struct ao_hmc5883_sample {
+       int16_t         x, y, z;
+};
+
+void
+ao_hmc5883_init(void);
+
+#endif /* _AO_HMC5883_H_ */
diff --git a/src/drivers/ao_lcd.c b/src/drivers/ao_lcd.c
new file mode 100644 (file)
index 0000000..6def0c8
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static uint16_t        ao_lcd_time = 3;
+
+static __xdata uint8_t ao_lcd_mutex;
+
+static void
+ao_lcd_delay(void)
+{
+       volatile uint16_t       count;
+
+       for (count = 0; count < ao_lcd_time; count++)
+               ;
+}
+
+static void
+ao_lcd_send_ins(uint8_t ins)
+{
+//     printf("send ins %02x\n", ins);
+//     ao_lcd_wait_idle();
+//     ao_delay(1);
+       ao_lcd_delay();
+       ao_lcd_port_put_nibble(0, ins >> 4);
+       ao_lcd_port_put_nibble(0, ins & 0xf);
+}
+
+static void
+ao_lcd_put_byte(uint8_t c)
+{
+//     printf ("send data %02x\n", c);
+//     ao_lcd_wait_idle();
+       ao_lcd_delay();
+       ao_lcd_port_put_nibble(1, c >> 4);
+       ao_lcd_port_put_nibble(1, c & 0x0f);
+}
+
+void
+ao_lcd_putstring(char *string)
+{
+       char    c;
+
+       ao_mutex_get(&ao_lcd_mutex);
+       while ((c = (uint8_t) *string++))
+               ao_lcd_put_byte((uint8_t) c);
+       ao_mutex_put(&ao_lcd_mutex);
+}
+
+#define AO_LCD_POWER_CONTROL   0x54
+
+void
+ao_lcd_contrast_set(uint8_t contrast)
+{
+       ao_mutex_get(&ao_lcd_mutex);
+       ao_lcd_send_ins(AO_LCD_POWER_CONTROL | ((contrast >> 4) & 0x3));
+       ao_lcd_send_ins(0x70 | (contrast & 0xf));
+       ao_mutex_put(&ao_lcd_mutex);
+}
+
+void
+ao_lcd_cursor_on(void)
+{
+       ao_mutex_get(&ao_lcd_mutex);
+       ao_lcd_send_ins(0x08 | 0x04 | 0x02 | 0x01);
+       ao_mutex_put(&ao_lcd_mutex);
+}
+
+void
+ao_lcd_cursor_off(void)
+{
+       ao_mutex_get(&ao_lcd_mutex);
+       ao_lcd_send_ins(0x08 | 0x04);
+       ao_mutex_put(&ao_lcd_mutex);
+}
+
+void
+ao_lcd_clear(void)
+{
+       ao_mutex_get(&ao_lcd_mutex);
+       ao_lcd_send_ins(0x01);
+       ao_delay(1);
+       /* Entry mode */
+       ao_lcd_send_ins(0x04 | 0x02);
+       ao_mutex_put(&ao_lcd_mutex);
+}
+
+void
+ao_lcd_goto(uint8_t addr)
+{
+       ao_mutex_get(&ao_lcd_mutex);
+       ao_lcd_send_ins(0x80 | addr);
+       ao_lcd_send_ins(0x04 | 0x02);
+       ao_mutex_put(&ao_lcd_mutex);
+}
+
+void
+ao_lcd_start(void)
+{
+       /* get to 4bit mode */
+       ao_lcd_port_put_nibble(0, 0x3);
+       ao_lcd_port_put_nibble(0, 0x3);
+       ao_lcd_port_put_nibble(0, 0x3);
+       ao_lcd_port_put_nibble(0, 0x2);
+
+       /* function set */
+       ao_lcd_send_ins(0x28);
+       /* function set, instruction table 1 */
+       ao_lcd_send_ins(0x29);
+
+       /* freq set */
+       ao_lcd_send_ins(0x14);
+
+       /* Power/icon/contrast control*/
+       ao_lcd_send_ins(AO_LCD_POWER_CONTROL);
+
+       /* Follower control */
+       ao_lcd_send_ins(0x6d);
+       ao_delay(AO_MS_TO_TICKS(200));
+
+       /* contrast set */
+       ao_lcd_contrast_set(0x18);
+
+       /* Display on */
+       ao_lcd_send_ins(0x08 | 0x04);
+
+       /* Clear */
+       ao_lcd_clear();
+}
+
+void
+ao_lcd_init(void)
+{
+       ao_lcd_port_init();
+}
diff --git a/src/drivers/ao_lco_cmd.c b/src/drivers/ao_lco_cmd.c
new file mode 100644 (file)
index 0000000..9c35b32
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_pad.h>
+#include <ao_lco_cmd.h>
+#include <ao_lco_func.h>
+#include <ao_radio_cmac.h>
+
+static __pdata uint16_t        lco_box;
+static __pdata uint8_t lco_channels;
+static __pdata uint16_t        tick_offset;
+
+static void
+lco_args(void) __reentrant
+{
+       ao_cmd_decimal();
+       lco_box = ao_cmd_lex_i;
+       ao_cmd_hex();
+       lco_channels = ao_cmd_lex_i;
+}
+
+static struct ao_pad_query     ao_pad_query;
+static uint16_t                        tick_offset;
+
+static int8_t
+lco_query(void)
+{
+       uint8_t i;
+       int8_t  r = AO_RADIO_CMAC_OK;
+
+       for (i = 0; i < 10; i++) {
+               printf ("."); flush();
+               r = ao_lco_query(lco_box, &ao_pad_query, &tick_offset);
+               if (r == AO_RADIO_CMAC_OK)
+                       break;
+       }
+       printf("\n"); flush();
+       return r;
+}
+
+static void
+lco_arm(void)
+{
+       ao_lco_arm(lco_box, lco_channels, tick_offset);
+}
+
+static void
+lco_ignite(void)
+{
+       ao_lco_ignite(lco_box, lco_channels, tick_offset);
+}
+
+static void
+lco_report_cmd(void) __reentrant
+{
+       int8_t          r;
+       uint8_t         c;
+
+       lco_args();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       r = lco_query();
+       switch (r) {
+       case AO_RADIO_CMAC_OK:
+               switch (ao_pad_query.arm_status) {
+               case AO_PAD_ARM_STATUS_ARMED:
+                       printf ("Armed: ");
+                       break;
+               case AO_PAD_ARM_STATUS_DISARMED:
+                       printf("Disarmed: ");
+                       break;
+               case AO_PAD_ARM_STATUS_UNKNOWN:
+               default:
+                       printf("Unknown: ");
+                       break;
+               }
+               for (c = 0; c < AO_PAD_MAX_CHANNELS; c++) {
+                       if (ao_pad_query.channels & (1 << c)) {
+                               printf (" pad %d ", c);
+                               switch (ao_pad_query.igniter_status[c]) {
+                               default:
+                                       printf("unknown, ");
+                                       break;
+                               case AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN:
+                                       printf("bad-open, ");
+                                       break;
+                               case AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN:
+                                       printf("good-igniter, ");
+                                       break;
+                               case AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_CLOSED:
+                                       printf("bad-closed, ");
+                                       break;
+                               }
+                       }
+               }
+               printf("Rssi: %d\n", ao_radio_cmac_rssi);
+               break;
+       default:
+               printf("Error %d\n", r);
+               break;
+       }
+}
+
+static void
+lco_fire_cmd(void) __reentrant
+{
+       static __xdata struct ao_pad_command    command;
+       uint8_t         secs;
+       uint8_t         i;
+       int8_t          r;
+
+       lco_args();
+       ao_cmd_decimal();
+       secs = ao_cmd_lex_i;
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       r = lco_query();
+       if (r != AO_RADIO_CMAC_OK) {
+               printf("query failed %d\n", r);
+               return;
+       }
+
+       for (i = 0; i < 4; i++) {
+               printf("arm %d\n", i); flush();
+               lco_arm();
+       }
+
+       secs = secs * 10 - 5;
+       if (secs > 100)
+               secs = 100;
+       for (i = 0; i < secs; i++) {
+               printf("fire %d\n", i); flush();
+               lco_ignite();
+               ao_delay(AO_MS_TO_TICKS(100));
+       }
+}
+
+static void
+lco_arm_cmd(void) __reentrant
+{
+       uint8_t i;
+       int8_t  r;
+       lco_args();
+       r = lco_query();
+       if (r != AO_RADIO_CMAC_OK) {
+               printf("query failed %d\n", r);
+               return;
+       }
+       for (i = 0; i < 4; i++)
+               lco_arm();
+}
+
+static void
+lco_ignite_cmd(void) __reentrant
+{
+       uint8_t i;
+       lco_args();
+       for (i = 0; i < 4; i++)
+               lco_ignite();
+}
+
+static __code struct ao_cmds ao_lco_cmds[] = {
+       { lco_report_cmd,       "l <box> <channel>\0Get remote status" },
+       { lco_fire_cmd,         "F <box> <channel> <secs>\0Fire remote igniters" },
+       { lco_arm_cmd,          "a <box> <channel>\0Arm remote igniter" },
+       { lco_ignite_cmd,       "i <box> <channel>\0Pulse remote igniter" },
+       { 0, NULL },
+};
+
+void
+ao_lco_cmd_init(void)
+{
+       ao_cmd_register(&ao_lco_cmds[0]);
+}
diff --git a/src/drivers/ao_lco_cmd.h b/src/drivers/ao_lco_cmd.h
new file mode 100644 (file)
index 0000000..c55448c
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LCO_CMD_H_
+#define _AO_LCO_CMD_H_
+
+void
+ao_lco_cmd_init(void);
+
+#endif /* _AO_LCO_CMD_H_ */
diff --git a/src/drivers/ao_lco_func.c b/src/drivers/ao_lco_func.c
new file mode 100644 (file)
index 0000000..99e58b7
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_pad.h>
+#include <ao_radio_cmac.h>
+#include <ao_lco_func.h>
+
+static __xdata struct ao_pad_command   command;
+static __xdata uint8_t                 ao_lco_mutex;
+
+int8_t
+ao_lco_query(uint16_t box, struct ao_pad_query *query, uint16_t *tick_offset)
+{
+       uint8_t         i;
+       int8_t          r;
+       uint16_t        sent_time;
+
+       ao_mutex_get(&ao_lco_mutex);
+       command.tick = ao_time() - *tick_offset;
+       command.box = box;
+       command.cmd = AO_LAUNCH_QUERY;
+       command.channels = 0;
+       ao_radio_cmac_send(&command, sizeof (command));
+       sent_time = ao_time();
+       r = ao_radio_cmac_recv(query, sizeof (*query), AO_MS_TO_TICKS(20));
+       if (r == AO_RADIO_CMAC_OK)
+               *tick_offset = sent_time - query->tick;
+       ao_mutex_put(&ao_lco_mutex);
+       return r;
+}
+
+void
+ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset)
+{
+       ao_mutex_get(&ao_lco_mutex);
+       command.tick = ao_time() - tick_offset;
+       command.box = box;
+       command.cmd = AO_LAUNCH_ARM;
+       command.channels = channels;
+       ao_radio_cmac_send(&command, sizeof (command));
+       ao_mutex_put(&ao_lco_mutex);
+}
+
+void
+ao_lco_ignite(uint16_t box, uint8_t channels, uint16_t tick_offset)
+{
+       ao_mutex_get(&ao_lco_mutex);
+       command.tick = ao_time() - tick_offset;
+       command.box = box;
+       command.cmd = AO_LAUNCH_FIRE;
+       command.channels = channels;
+       ao_radio_cmac_send(&command, sizeof (command));
+       ao_mutex_put(&ao_lco_mutex);
+}
+
diff --git a/src/drivers/ao_lco_func.h b/src/drivers/ao_lco_func.h
new file mode 100644 (file)
index 0000000..dccf602
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LCO_FUNC_H_
+#define _AO_LCO_FUNC_H_
+
+#include <ao_pad.h>
+
+int8_t
+ao_lco_query(uint16_t box, struct ao_pad_query *query, uint16_t *tick_offset);
+
+void
+ao_lco_arm(uint16_t box, uint8_t channels, uint16_t tick_offset);
+
+void
+ao_lco_ignite(uint16_t box, uint8_t channels, uint16_t tick_offset);
+
+#endif /* _AO_LCO_FUNC_H_ */
diff --git a/src/drivers/ao_m25.c b/src/drivers/ao_m25.c
new file mode 100644 (file)
index 0000000..9603c1d
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * 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.
+ */
+
+#include "ao.h"
+
+/* Total bytes of available storage */
+__pdata uint32_t       ao_storage_total;
+
+/* Block size - device is erased in these units. At least 256 bytes */
+__pdata uint32_t       ao_storage_block;
+
+/* Byte offset of config block. Will be ao_storage_block bytes long */
+__pdata uint32_t       ao_storage_config;
+
+/* Storage unit size - device reads and writes must be within blocks of this size. Usually 256 bytes. */
+__pdata uint16_t       ao_storage_unit;
+
+/*
+ * Each flash chip is arranged in 64kB sectors; the
+ * chip cannot erase in units smaller than that.
+ *
+ * Writing happens in units of 256 byte pages and
+ * can only change bits from 1 to 0. So, you can rewrite
+ * the same contents, or append to an existing page easily enough
+ */
+
+#define M25_WREN       0x06    /* Write Enable */
+#define M25_WRDI       0x04    /* Write Disable */
+#define M25_RDID       0x9f    /* Read Identification */
+#define M25_RDSR       0x05    /* Read Status Register */
+#define M25_WRSR       0x01    /* Write Status Register */
+#define M25_READ       0x03    /* Read Data Bytes */
+#define M25_FAST_READ  0x0b    /* Read Data Bytes at Higher Speed */
+#define M25_PP         0x02    /* Page Program */
+#define M25_SE         0xd8    /* Sector Erase */
+#define M25_BE         0xc7    /* Bulk Erase */
+#define M25_DP         0xb9    /* Deep Power-down */
+
+/* RDID response */
+#define M25_MANUF_OFFSET       0
+#define M25_MEMORY_TYPE_OFFSET 1
+#define M25_CAPACITY_OFFSET    2
+#define M25_UID_OFFSET         3
+#define M25_CFI_OFFSET         4
+#define M25_RDID_LEN           4       /* that's all we need */
+
+#define M25_CAPACITY_128KB     0x11
+#define M25_CAPACITY_256KB     0x12
+#define M25_CAPACITY_512KB     0x13
+#define M25_CAPACITY_1MB       0x14
+#define M25_CAPACITY_2MB       0x15
+
+/*
+ * Status register bits
+ */
+
+#define M25_STATUS_SRWD                (1 << 7)        /* Status register write disable */
+#define M25_STATUS_BP_MASK     (7 << 2)        /* Block protect bits */
+#define M25_STATUS_BP_SHIFT    (2)
+#define M25_STATUS_WEL         (1 << 1)        /* Write enable latch */
+#define M25_STATUS_WIP         (1 << 0)        /* Write in progress */
+
+/*
+ * On teleterra, the m25 chip select pins are
+ * wired on P0_0 through P0_3.
+ */
+
+#if M25_MAX_CHIPS > 1
+static uint8_t ao_m25_size[M25_MAX_CHIPS];     /* number of sectors in each chip */
+static uint8_t ao_m25_pin[M25_MAX_CHIPS];      /* chip select pin for each chip */
+static uint8_t ao_m25_numchips;                        /* number of chips detected */
+#endif
+static uint8_t ao_m25_total;                   /* total sectors available */
+static uint8_t ao_m25_wip;                     /* write in progress */
+
+static __xdata uint8_t ao_m25_mutex;
+
+/*
+ * This little array is abused to send and receive data. A particular
+ * caution -- the read and write addresses are written into the last
+ * three bytes of the array by ao_m25_set_page_address and then the
+ * first byte is used by ao_m25_wait_wip and ao_m25_write_enable, neither
+ * of which touch those last three bytes.
+ */
+
+static __xdata uint8_t ao_m25_instruction[4];
+
+#if HAS_BOOT_RADIO
+extern uint8_t ao_radio_in_recv;
+
+static void ao_boot_radio(void) {
+       if (ao_radio_in_recv)
+               ao_radio_recv_abort();
+}
+#else
+#define ao_boot_radio()
+#endif
+
+#define M25_SELECT(cs)         do { ao_boot_radio(); ao_spi_get_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS, AO_SPI_SPEED_FAST); } while (0)
+#define M25_DESELECT(cs)       ao_spi_put_mask(AO_M25_SPI_CS_PORT,cs,AO_M25_SPI_BUS)
+
+#define M25_BLOCK_SHIFT                        16
+#define M25_BLOCK                      65536L
+#define M25_POS_TO_SECTOR(pos)         ((uint8_t) ((pos) >> M25_BLOCK_SHIFT))
+#define M25_SECTOR_TO_POS(sector)      (((uint32_t) (sector)) << M25_BLOCK_SHIFT)
+
+/*
+ * Block until the specified chip is done writing
+ */
+static void
+ao_m25_wait_wip(uint8_t cs)
+{
+       if (ao_m25_wip & cs) {
+               M25_SELECT(cs);
+               ao_m25_instruction[0] = M25_RDSR;
+               ao_spi_send(ao_m25_instruction, 1, AO_M25_SPI_BUS);
+               do {
+                       ao_spi_recv(ao_m25_instruction, 1, AO_M25_SPI_BUS);
+               } while (ao_m25_instruction[0] & M25_STATUS_WIP);
+               M25_DESELECT(cs);
+               ao_m25_wip &= ~cs;
+       }
+}
+
+/*
+ * Set the write enable latch so that page program and sector
+ * erase commands will work. Also mark the chip as busy writing
+ * so that future operations will block until the WIP bit goes off
+ */
+static void
+ao_m25_write_enable(uint8_t cs)
+{
+       M25_SELECT(cs);
+       ao_m25_instruction[0] = M25_WREN;
+       ao_spi_send(&ao_m25_instruction, 1, AO_M25_SPI_BUS);
+       M25_DESELECT(cs);
+       ao_m25_wip |= cs;
+}
+
+
+/*
+ * Returns the number of 64kB sectors
+ */
+static uint8_t
+ao_m25_read_capacity(uint8_t cs)
+{
+       uint8_t capacity;
+       M25_SELECT(cs);
+       ao_m25_instruction[0] = M25_RDID;
+       ao_spi_send(ao_m25_instruction, 1, AO_M25_SPI_BUS);
+       ao_spi_recv(ao_m25_instruction, M25_RDID_LEN, AO_M25_SPI_BUS);
+       M25_DESELECT(cs);
+
+       /* Check to see if the chip is present */
+       if (ao_m25_instruction[0] == 0xff)
+               return 0;
+       capacity = ao_m25_instruction[M25_CAPACITY_OFFSET];
+
+       /* Sanity check capacity number */
+       if (capacity < 0x11 || 0x1f < capacity)
+               return 0;
+       return 1 << (capacity - 0x10);
+}
+
+static uint8_t
+ao_m25_set_address(uint32_t pos)
+{
+       uint8_t chip;
+#if M25_MAX_CHIPS > 1
+       uint8_t size;
+
+       for (chip = 0; chip < ao_m25_numchips; chip++) {
+               size = ao_m25_size[chip];
+               if (M25_POS_TO_SECTOR(pos) < size)
+                       break;
+               pos -= M25_SECTOR_TO_POS(size);
+       }
+       if (chip == ao_m25_numchips)
+               return 0xff;
+
+       chip = ao_m25_pin[chip];
+#else
+       chip = AO_M25_SPI_CS_MASK;
+#endif
+       ao_m25_wait_wip(chip);
+
+       ao_m25_instruction[1] = pos >> 16;
+       ao_m25_instruction[2] = pos >> 8;
+       ao_m25_instruction[3] = pos;
+       return chip;
+}
+
+/*
+ * Scan the possible chip select lines
+ * to see which flash chips are connected
+ */
+static uint8_t
+ao_m25_scan(void)
+{
+#if M25_MAX_CHIPS > 1
+       uint8_t pin, size;
+#endif
+
+       if (ao_m25_total)
+               return 1;
+
+#if M25_MAX_CHIPS > 1
+       ao_m25_numchips = 0;
+       for (pin = 1; pin != 0; pin <<= 1) {
+               if (AO_M25_SPI_CS_MASK & pin) {
+                       size = ao_m25_read_capacity(pin);
+                       if (size != 0) {
+                               ao_m25_size[ao_m25_numchips] = size;
+                               ao_m25_pin[ao_m25_numchips] = pin;
+                               ao_m25_total += size;
+                               ao_m25_numchips++;
+                       }
+               }
+       }
+#else
+       ao_m25_total = ao_m25_read_capacity(AO_M25_SPI_CS_MASK);
+#endif
+       if (!ao_m25_total)
+               return 0;
+       ao_storage_total = M25_SECTOR_TO_POS(ao_m25_total);
+       ao_storage_block = M25_BLOCK;
+       ao_storage_config = ao_storage_total - M25_BLOCK;
+       ao_storage_unit = 256;
+       return 1;
+}
+
+/*
+ * Erase the specified sector
+ */
+uint8_t
+ao_storage_erase(uint32_t pos) __reentrant
+{
+       uint8_t cs;
+
+       if (pos >= ao_storage_total || pos + ao_storage_block > ao_storage_total)
+               return 0;
+
+       ao_mutex_get(&ao_m25_mutex);
+       ao_m25_scan();
+
+       cs = ao_m25_set_address(pos);
+
+       ao_m25_wait_wip(cs);
+       ao_m25_write_enable(cs);
+
+       ao_m25_instruction[0] = M25_SE;
+       M25_SELECT(cs);
+       ao_spi_send(ao_m25_instruction, 4, AO_M25_SPI_BUS);
+       M25_DESELECT(cs);
+       ao_m25_wip |= cs;
+
+       ao_mutex_put(&ao_m25_mutex);
+       return 1;
+}
+
+/*
+ * Write to flash
+ */
+uint8_t
+ao_storage_device_write(uint32_t pos, __xdata void *d, uint16_t len) __reentrant
+{
+       uint8_t cs;
+
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+
+       ao_mutex_get(&ao_m25_mutex);
+       ao_m25_scan();
+
+       cs = ao_m25_set_address(pos);
+       ao_m25_write_enable(cs);
+
+       ao_m25_instruction[0] = M25_PP;
+       M25_SELECT(cs);
+       ao_spi_send(ao_m25_instruction, 4, AO_M25_SPI_BUS);
+       ao_spi_send(d, len, AO_M25_SPI_BUS);
+       M25_DESELECT(cs);
+
+       ao_mutex_put(&ao_m25_mutex);
+       return 1;
+}
+
+/*
+ * Read from flash
+ */
+uint8_t
+ao_storage_device_read(uint32_t pos, __xdata void *d, uint16_t len) __reentrant
+{
+       uint8_t cs;
+
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+       ao_mutex_get(&ao_m25_mutex);
+       ao_m25_scan();
+
+       cs = ao_m25_set_address(pos);
+
+       /* No need to use the FAST_READ as we're running at only 8MHz */
+       ao_m25_instruction[0] = M25_READ;
+       M25_SELECT(cs);
+       ao_spi_send(ao_m25_instruction, 4, AO_M25_SPI_BUS);
+       ao_spi_recv(d, len, AO_M25_SPI_BUS);
+       M25_DESELECT(cs);
+
+       ao_mutex_put(&ao_m25_mutex);
+       return 1;
+}
+
+void
+ao_storage_flush(void) __reentrant
+{
+}
+
+void
+ao_storage_setup(void)
+{
+       ao_mutex_get(&ao_m25_mutex);
+       ao_m25_scan();
+       ao_mutex_put(&ao_m25_mutex);
+}
+
+void
+ao_storage_device_info(void) __reentrant
+{
+       uint8_t cs;
+#if M25_MAX_CHIPS > 1
+       uint8_t chip;
+#endif
+
+       ao_mutex_get(&ao_m25_mutex);
+       ao_m25_scan();
+       ao_mutex_put(&ao_m25_mutex);
+
+#if M25_MAX_CHIPS > 1
+       printf ("Detected chips %d size %d\n", ao_m25_numchips, ao_m25_total);
+       for (chip = 0; chip < ao_m25_numchips; chip++)
+               printf ("Flash chip %d select %02x size %d\n",
+                       chip, ao_m25_pin[chip], ao_m25_size[chip]);
+#else
+       printf ("Detected chips 1 size %d\n", ao_m25_total);
+#endif
+
+       printf ("Available chips:\n");
+       for (cs = 1; cs != 0; cs <<= 1) {
+               if ((AO_M25_SPI_CS_MASK & cs) == 0)
+                       continue;
+
+               ao_mutex_get(&ao_m25_mutex);
+               M25_SELECT(cs);
+               ao_m25_instruction[0] = M25_RDID;
+               ao_spi_send(ao_m25_instruction, 1, AO_M25_SPI_BUS);
+               ao_spi_recv(ao_m25_instruction, M25_RDID_LEN, AO_M25_SPI_BUS);
+               M25_DESELECT(cs);
+
+               printf ("Select %02x manf %02x type %02x cap %02x uid %02x\n",
+                       cs,
+                       ao_m25_instruction[M25_MANUF_OFFSET],
+                       ao_m25_instruction[M25_MEMORY_TYPE_OFFSET],
+                       ao_m25_instruction[M25_CAPACITY_OFFSET],
+                       ao_m25_instruction[M25_UID_OFFSET]);
+               ao_mutex_put(&ao_m25_mutex);
+       }
+}
+
+void
+ao_storage_device_init(void)
+{
+       ao_spi_init_cs (AO_M25_SPI_CS_PORT, AO_M25_SPI_CS_MASK);
+}
diff --git a/src/drivers/ao_mma655x.c b/src/drivers/ao_mma655x.c
new file mode 100644 (file)
index 0000000..0642220
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_mma655x.h>
+
+#if HAS_MMA655X
+
+#if 1
+#define PRINTD(...) do { printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); } while(0)
+#else
+#define PRINTD(...) 
+#endif
+
+static uint8_t mma655x_configured;
+
+uint8_t        ao_mma655x_spi_index = AO_MMA655X_SPI_INDEX;
+
+static void
+ao_mma655x_start(void) {
+       ao_spi_get_bit(AO_MMA655X_CS_PORT,
+                      AO_MMA655X_CS_PIN,
+                      AO_MMA655X_CS,
+                      AO_MMA655X_SPI_INDEX,
+                      AO_SPI_SPEED_FAST);
+}
+
+static void
+ao_mma655x_stop(void) {
+       ao_spi_put_bit(AO_MMA655X_CS_PORT,
+                      AO_MMA655X_CS_PIN,
+                      AO_MMA655X_CS,
+                      AO_MMA655X_SPI_INDEX);
+}
+
+static void
+ao_mma655x_restart(void) {
+       uint8_t i;
+       ao_gpio_set(AO_MMA655X_CS_PORT, AO_MMA655X_CS_PIN, AO_MMA655X_CS, 1);
+
+       /* Emperical testing on STM32L151 at 32MHz for this delay amount */
+       for (i = 0; i < 9; i++)
+               ao_arch_nop();
+       ao_gpio_set(AO_MMA655X_CS_PORT, AO_MMA655X_CS_PIN, AO_MMA655X_CS, 0);
+}
+
+static uint8_t
+ao_parity(uint8_t v)
+{
+       uint8_t p;
+       /* down to four bits */
+       p = (v ^ (v >> 4)) & 0xf;
+
+       /* Cute lookup hack -- 0x6996 encodes the sixteen
+        * even parity values in order.
+        */
+       p = (~0x6996 >> p) & 1;
+       return p;
+}
+
+static void
+ao_mma655x_cmd(uint8_t d[2])
+{
+       ao_mma655x_start();
+       PRINTD("\tSEND %02x %02x\n", d[0], d[1]);
+       ao_spi_duplex(d, d, 2, AO_MMA655X_SPI_INDEX);
+       PRINTD("\t\tRECV %02x %02x\n", d[0], d[1]);
+       ao_mma655x_stop();
+}
+
+static uint8_t
+ao_mma655x_reg_read(uint8_t addr)
+{
+       uint8_t d[2];
+       ao_mma655x_start();
+       d[0] = addr | (ao_parity(addr) << 7);
+       d[1] = 0;
+       ao_spi_send(&d, 2, AO_MMA655X_SPI_INDEX);
+       ao_mma655x_restart();
+
+       /* Send a dummy read of 00 to clock out the SPI data */
+       d[0] = 0x80;
+       d[1] = 0x00;
+       ao_spi_duplex(&d, &d, 2, AO_MMA655X_SPI_INDEX);
+       ao_mma655x_stop();
+       return d[1];
+}
+
+static void
+ao_mma655x_reg_write(uint8_t addr, uint8_t value)
+{
+       uint8_t d[2];
+
+       addr |= (1 << 6);       /* write mode */
+       d[0] = addr | (ao_parity(addr^value) << 7);
+       d[1] = value;
+       ao_mma655x_start();
+       ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
+       ao_mma655x_stop();
+
+       addr &= ~(1 << 6);
+       PRINTD("write %x %x = %x\n",
+              addr, value, ao_mma655x_reg_read(addr));
+}
+
+static uint16_t
+ao_mma655x_value(void)
+{
+       uint8_t         d[2];
+       uint16_t        v;
+
+       d[0] = ((0 << 6) |      /* Axis selection (X) */
+               (1 << 5) |      /* Acceleration operation */
+               (1 << 4));      /* Raw data */
+       d[1] = ((1 << 3) |      /* must be one */
+               (1 << 2) |      /* Unsigned data */
+               (0 << 1) |      /* Arm disabled */
+               (1 << 0));      /* Odd parity */
+       ao_mma655x_start();
+       PRINTD("value SEND %02x %02x\n", d[0], d[1]);
+       ao_spi_send(d, 2, AO_MMA655X_SPI_INDEX);
+       ao_mma655x_restart();
+       d[0] = 0x80;
+       d[1] = 0x00;
+       ao_spi_duplex(d, d, 2, AO_MMA655X_SPI_INDEX);
+       ao_mma655x_stop();
+       PRINTD("value RECV %02x %02x\n", d[0], d[1]);
+
+       v = (uint16_t) d[1] << 2;
+       v |= d[0] >> 6;
+       v |= (uint16_t) (d[0] & 3) << 10;
+       return v;
+}
+
+static void
+ao_mma655x_reset(void) {
+       ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
+                            (0 << AO_MMA655X_DEVCTL_RES_1) |
+                            (0 << AO_MMA655X_DEVCTL_RES_1));
+       ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
+                            (1 << AO_MMA655X_DEVCTL_RES_1) |
+                            (1 << AO_MMA655X_DEVCTL_RES_1));
+       ao_mma655x_reg_write(AO_MMA655X_DEVCTL,
+                            (0 << AO_MMA655X_DEVCTL_RES_1) |
+                            (1 << AO_MMA655X_DEVCTL_RES_1));
+}
+
+#define DEVCFG_VALUE   (\
+       (1 << AO_MMA655X_DEVCFG_OC) |           /* Disable offset cancelation */ \
+       (1 << AO_MMA655X_DEVCFG_SD) |           /* Receive unsigned data */ \
+       (0 << AO_MMA655X_DEVCFG_OFMON) |        /* Disable offset monitor */ \
+       (AO_MMA655X_DEVCFG_A_CFG_DISABLE << AO_MMA655X_DEVCFG_A_CFG))
+
+#define AXISCFG_VALUE  (\
+               (0 << AO_MMA655X_AXISCFG_LPF))  /* 100Hz 4-pole filter */
+
+
+static void
+ao_mma655x_setup(void)
+{
+       uint8_t         v;
+       uint16_t        a, a_st;
+       uint8_t         stdefl;
+       uint8_t         i;
+       uint8_t s0, s1, s2, s3;
+       uint8_t pn;
+       uint32_t        lot;
+       uint16_t        serial;
+
+
+       if (mma655x_configured)
+               return;
+       mma655x_configured = 1;
+       ao_delay(AO_MS_TO_TICKS(10));   /* Top */
+       ao_mma655x_reset();
+       ao_delay(AO_MS_TO_TICKS(10));   /* Top */
+       (void) ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
+       v = ao_mma655x_reg_read(AO_MMA655X_DEVSTAT);
+
+       /* Configure R/W register values.
+        * Most of them relate to the arming feature, which
+        * we don't use, so the only registers we need to
+        * write are DEVCFG and AXISCFG
+        */
+
+       ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
+                            DEVCFG_VALUE | (0 << AO_MMA655X_DEVCFG_ENDINIT));
+
+       /* Test X axis
+        */
+       
+       ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
+                            AXISCFG_VALUE |
+                            (1 << AO_MMA655X_AXISCFG_ST));
+       for (i = 0; i < 10; i++) {
+               a_st = ao_mma655x_value();
+               printf ("SELF-TEST %2d = %6d\n", i, a_st);
+       }
+
+       stdefl = ao_mma655x_reg_read(AO_MMA655X_STDEFL);
+
+       ao_mma655x_reg_write(AO_MMA655X_AXISCFG,
+                            AXISCFG_VALUE |
+                            (0 << AO_MMA655X_AXISCFG_ST));
+       a = ao_mma655x_value();
+
+       for (i = 0; i < 10; i++) {
+               a = ao_mma655x_value();
+               printf("NORMAL   %2d = %6d\n", i, a);
+       }
+
+       ao_mma655x_reg_write(AO_MMA655X_DEVCFG,
+                            DEVCFG_VALUE | (1 << AO_MMA655X_DEVCFG_ENDINIT));
+       s0 = ao_mma655x_reg_read(AO_MMA655X_SN0);
+       s1 = ao_mma655x_reg_read(AO_MMA655X_SN1);
+       s2 = ao_mma655x_reg_read(AO_MMA655X_SN2);
+       s3 = ao_mma655x_reg_read(AO_MMA655X_SN3);
+       lot = ((uint32_t) s3 << 24) | ((uint32_t) s2 << 16) |
+               ((uint32_t) s1 << 8) | ((uint32_t) s0);
+       serial = lot & 0x1fff;
+       lot >>= 12;
+       pn = ao_mma655x_reg_read(AO_MMA655X_PN);
+       printf ("MMA655X lot %d serial %d number %d\n", lot, serial, pn);
+
+}
+
+static void
+ao_mma655x_dump(void)
+{
+       ao_mma655x_setup();
+       printf ("MMA655X value %d\n", ao_mma655x_value());
+}
+
+__code struct ao_cmds ao_mma655x_cmds[] = {
+       { ao_mma655x_dump,      "A\0Display MMA655X data" },
+       { 0, NULL },
+};
+
+static void
+ao_mma655x(void)
+{
+       ao_mma655x_setup();
+       for (;;) {
+               ao_data_ring[ao_data_head].mma655x = ao_mma655x_value();
+               ao_arch_critical(
+                       AO_DATA_PRESENT(AO_DATA_MMA655X);
+                       AO_DATA_WAIT();
+                       );
+       }
+}
+
+static __xdata struct ao_task ao_mma655x_task;
+
+void
+ao_mma655x_init(void)
+{
+       mma655x_configured = 0;
+
+       ao_cmd_register(&ao_mma655x_cmds[0]);
+       ao_spi_init_cs(AO_MMA655X_CS_PORT, (1 << AO_MMA655X_CS_PIN));
+
+//     ao_add_task(&ao_mma655x_task, ao_mma655x, "mma655x");
+}
+
+#endif
diff --git a/src/drivers/ao_mma655x.h b/src/drivers/ao_mma655x.h
new file mode 100644 (file)
index 0000000..9c0c59d
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_MMA655X_H_
+#define _AO_MMA655X_H_
+
+
+#define AO_MMA655X_SN0         0x00
+#define AO_MMA655X_SN1         0x01
+#define AO_MMA655X_SN2         0x02
+#define AO_MMA655X_SN3         0x03
+#define AO_MMA655X_SN3         0x03
+#define AO_MMA655X_STDEFL      0x04
+#define AO_MMA655X_FCTCFG      0x06
+# define AO_MMA655X_FCTCFG_STMAG       7
+
+#define AO_MMA655X_PN          0x08
+#define AO_MMA655X_DEVCTL      0x0a
+#define AO_MMA655X_DEVCTL_RES_1                7
+#define AO_MMA655X_DEVCTL_RES_0                6
+#define AO_MMA655X_DEVCTL_OCPHASE      4
+#define AO_MMA655X_DEVCTL_OCPHASE_MASK 3
+#define AO_MMA655X_DEVCTL_OFFCFG_EN    3
+
+#define AO_MMA655X_DEVCFG      0x0b
+#define AO_MMA655X_DEVCFG_OC           7
+#define AO_MMA655X_DEVCFG_ENDINIT      5
+#define AO_MMA655X_DEVCFG_SD           4
+#define AO_MMA655X_DEVCFG_OFMON                3
+#define AO_MMA655X_DEVCFG_A_CFG                0
+#define AO_MMA655X_DEVCFG_A_CFG_DISABLE                0
+#define AO_MMA655X_DEVCFG_A_CFG_PCM            1
+#define AO_MMA655X_DEVCFG_A_CFG_MOVING_AVG_HIGH        2
+#define AO_MMA655X_DEVCFG_A_CFG_MOVING_AVG_LOW 3
+#define AO_MMA655X_DEVCFG_A_CFG_COUNT_HIGH     4
+#define AO_MMA655X_DEVCFG_A_CFG_COUNT_LOW      5
+#define AO_MMA655X_DEVCFG_A_CFG_UNFILTERED_HIGH        6
+#define AO_MMA655X_DEVCFG_A_CFG_UNFILTERED_LOW 7
+#define AO_MMA655X_DEVCFG_A_CFG_MASK           7
+
+#define AO_MMA655X_AXISCFG     0x0c
+#define AO_MMA655X_AXISCFG_ST          7
+#define AO_MMA655X_AXISCFG_LPF         0
+#define AO_MMA655X_AXISCFG_LPF_MASK    0xf
+
+#define AO_MMA655X_ARMCFG      0x0e
+#define AO_MMA655X_ARMCFG_APS          4
+#define AO_MMA655X_ARMCFG_APS_MASK     3
+#define AO_MMA655X_ARMCFG_AWS_N                2
+#define AO_MMA655X_ARMCFG_AWS_N_MASK   3
+#define AO_MMA655X_ARMCFG_AWS_P                0
+#define AO_MMA655X_ARMCFG_AWS_P_MASK   3
+
+#define AO_MMA655X_ARMT_P      0x10
+#define AO_MMA655X_ARMT_N      0x12
+
+#define AO_MMA655X_DEVSTAT     0x14
+#define AO_MMA655X_DEVSTAT_IDE         6
+#define AO_MMA655X_DEVSTAT_DEVINIT     4
+#define AO_MMA655X_DEVSTAT_MISOERR     3
+#define AO_MMA655X_DEVSTAT_OFFSET      1
+#define AO_MMA655X_DEVSTAT_DEVRES      0
+
+#define AO_MMA655X_COUNT       0x15
+#define AO_MMA655X_OFFCORR     0x16
+
+
+void
+ao_mma655x_init(void);
+
+#endif /* _AO_MMA655X_H_ */
diff --git a/src/drivers/ao_mpu6000.c b/src/drivers/ao_mpu6000.c
new file mode 100644 (file)
index 0000000..b3e284e
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_mpu6000.h>
+#include <ao_exti.h>
+
+static uint8_t ao_mpu6000_wake;
+static uint8_t ao_mpu6000_configured;
+
+static void
+ao_mpu6000_write(uint8_t addr, uint8_t *data, uint8_t len)
+{
+       ao_i2c_get(AO_MPU6000_I2C_INDEX);
+       ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE);
+       ao_i2c_send(&addr, 1, AO_MPU6000_I2C_INDEX, FALSE);
+       ao_i2c_send(data, len, AO_MPU6000_I2C_INDEX, TRUE);
+       ao_i2c_put(AO_MPU6000_I2C_INDEX);
+}
+
+static void
+ao_mpu6000_reg_write(uint8_t addr, uint8_t value)
+{
+       uint8_t d[2] = { addr, value };
+       ao_i2c_get(AO_MPU6000_I2C_INDEX);
+       ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE);
+       ao_i2c_send(d, 2, AO_MPU6000_I2C_INDEX, TRUE);
+       ao_i2c_put(AO_MPU6000_I2C_INDEX);
+}
+
+static void
+ao_mpu6000_read(uint8_t addr, void *data, uint8_t len)
+{
+       ao_i2c_get(AO_MPU6000_I2C_INDEX);
+       ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE);
+       ao_i2c_send(&addr, 1, AO_MPU6000_I2C_INDEX, FALSE);
+       ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_READ);
+       ao_i2c_recv(data, len, AO_MPU6000_I2C_INDEX, TRUE);
+       ao_i2c_put(AO_MPU6000_I2C_INDEX);
+}
+
+static uint8_t
+ao_mpu6000_reg_read(uint8_t addr)
+{
+       uint8_t value;
+       ao_i2c_get(AO_MPU6000_I2C_INDEX);
+       ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_WRITE);
+       ao_i2c_send(&addr, 1, AO_MPU6000_I2C_INDEX, FALSE);
+       ao_i2c_start(AO_MPU6000_I2C_INDEX, MPU6000_ADDR_READ);
+       ao_i2c_recv(&value, 1, AO_MPU6000_I2C_INDEX, TRUE);
+       ao_i2c_put(AO_MPU6000_I2C_INDEX);
+       return value;
+}
+
+static void
+ao_mpu6000_sample(struct ao_mpu6000_sample *sample)
+{
+       uint16_t        *d = (uint16_t *) sample;
+       int             i = sizeof (*sample) / 2;
+
+       ao_mpu6000_read(MPU6000_ACCEL_XOUT_H, sample, sizeof (*sample));
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       /* byte swap */
+       while (i--) {
+               uint16_t        t = *d;
+               *d++ = (t >> 8) | (t << 8);
+       }
+#endif
+}
+
+#define G      981     /* in cm/s² */
+
+static int16_t /* cm/s² */
+ao_mpu6000_accel(int16_t v)
+{
+       return (int16_t) ((v * (int32_t) (16.0 * 980.665 + 0.5)) / 32767);
+}
+
+static int16_t /* deg*10/s */
+ao_mpu6000_gyro(int16_t v)
+{
+       return (int16_t) ((v * (int32_t) 20000) / 32767);
+}
+
+static uint8_t
+ao_mpu6000_accel_check(int16_t normal, int16_t test, char *which)
+{
+       int16_t diff = test - normal;
+
+       if (diff < MPU6000_ST_ACCEL(16) / 2) {
+               return 1;
+       }
+       if (diff > MPU6000_ST_ACCEL(16) * 2) {
+               return 1;
+       }
+       return 0;
+}
+
+static uint8_t
+ao_mpu6000_gyro_check(int16_t normal, int16_t test, char *which)
+{
+       int16_t diff = test - normal;
+
+       if (diff < 0)
+               diff = -diff;
+       if (diff < MPU6000_ST_GYRO(2000) / 2) {
+               return 1;
+       }
+       if (diff > MPU6000_ST_GYRO(2000) * 2) {
+               return 1;
+       }
+       return 0;
+}
+
+static void
+ao_mpu6000_setup(void)
+{
+       struct ao_mpu6000_sample        normal_mode, test_mode;
+       int                             errors =0;
+
+       if (ao_mpu6000_configured)
+               return;
+
+       /* Reset the whole chip */
+       
+       ao_mpu6000_reg_write(MPU6000_PWR_MGMT_1,
+                            (1 << MPU6000_PWR_MGMT_1_DEVICE_RESET));
+
+       /* Wait for it to reset. If we talk too quickly, it appears to get confused */
+       ao_delay(AO_MS_TO_TICKS(100));
+
+       /* Reset signal conditioning */
+       ao_mpu6000_reg_write(MPU6000_USER_CONTROL,
+                            (0 << MPU6000_USER_CONTROL_FIFO_EN) |
+                            (0 << MPU6000_USER_CONTROL_I2C_MST_EN) |
+                            (0 << MPU6000_USER_CONTROL_I2C_IF_DIS) |
+                            (0 << MPU6000_USER_CONTROL_FIFO_RESET) |
+                            (0 << MPU6000_USER_CONTROL_I2C_MST_RESET) |
+                            (1 << MPU6000_USER_CONTROL_SIG_COND_RESET));
+
+       while (ao_mpu6000_reg_read(MPU6000_USER_CONTROL) & (1 << MPU6000_USER_CONTROL_SIG_COND_RESET))
+               ao_yield();
+
+       /* Reset signal paths */
+       ao_mpu6000_reg_write(MPU6000_SIGNAL_PATH_RESET,
+                            (1 << MPU6000_SIGNAL_PATH_RESET_GYRO_RESET) |
+                            (1 << MPU6000_SIGNAL_PATH_RESET_ACCEL_RESET) |
+                            (1 << MPU6000_SIGNAL_PATH_RESET_TEMP_RESET));
+
+       ao_mpu6000_reg_write(MPU6000_SIGNAL_PATH_RESET,
+                            (0 << MPU6000_SIGNAL_PATH_RESET_GYRO_RESET) |
+                            (0 << MPU6000_SIGNAL_PATH_RESET_ACCEL_RESET) |
+                            (0 << MPU6000_SIGNAL_PATH_RESET_TEMP_RESET));
+
+       /* Select clocks, disable sleep */
+       ao_mpu6000_reg_write(MPU6000_PWR_MGMT_1,
+                            (0 << MPU6000_PWR_MGMT_1_DEVICE_RESET) |
+                            (0 << MPU6000_PWR_MGMT_1_SLEEP) |
+                            (0 << MPU6000_PWR_MGMT_1_CYCLE) |
+                            (0 << MPU6000_PWR_MGMT_1_TEMP_DIS) |
+                            (MPU6000_PWR_MGMT_1_CLKSEL_PLL_X_AXIS << MPU6000_PWR_MGMT_1_CLKSEL));
+
+       /* Set sample rate divider to sample at full speed
+       ao_mpu6000_reg_write(MPU6000_SMPRT_DIV, 0);
+
+       /* Disable filtering */
+       ao_mpu6000_reg_write(MPU6000_CONFIG,
+                            (MPU6000_CONFIG_EXT_SYNC_SET_DISABLED << MPU6000_CONFIG_EXT_SYNC_SET) |
+                            (MPU6000_CONFIG_DLPF_CFG_260_256 << MPU6000_CONFIG_DLPF_CFG));
+
+       /* Configure accelerometer to +/-16G in self-test mode */
+       ao_mpu6000_reg_write(MPU6000_ACCEL_CONFIG,
+                            (1 << MPU600_ACCEL_CONFIG_XA_ST) |
+                            (1 << MPU600_ACCEL_CONFIG_YA_ST) |
+                            (1 << MPU600_ACCEL_CONFIG_ZA_ST) |
+                            (MPU600_ACCEL_CONFIG_AFS_SEL_16G << MPU600_ACCEL_CONFIG_AFS_SEL));
+
+       /* Configure gyro to +/- 2000°/s in self-test mode */
+       ao_mpu6000_reg_write(MPU6000_GYRO_CONFIG,
+                            (1 << MPU600_GYRO_CONFIG_XG_ST) |
+                            (1 << MPU600_GYRO_CONFIG_YG_ST) |
+                            (1 << MPU600_GYRO_CONFIG_ZG_ST) |
+                            (MPU600_GYRO_CONFIG_FS_SEL_2000 << MPU600_GYRO_CONFIG_FS_SEL));
+
+       ao_delay(AO_MS_TO_TICKS(200));
+       ao_mpu6000_sample(&test_mode);
+
+       /* Configure accelerometer to +/-16G */
+       ao_mpu6000_reg_write(MPU6000_ACCEL_CONFIG,
+                            (0 << MPU600_ACCEL_CONFIG_XA_ST) |
+                            (0 << MPU600_ACCEL_CONFIG_YA_ST) |
+                            (0 << MPU600_ACCEL_CONFIG_ZA_ST) |
+                            (MPU600_ACCEL_CONFIG_AFS_SEL_16G << MPU600_ACCEL_CONFIG_AFS_SEL));
+
+       /* Configure gyro to +/- 2000°/s */
+       ao_mpu6000_reg_write(MPU6000_GYRO_CONFIG,
+                            (0 << MPU600_GYRO_CONFIG_XG_ST) |
+                            (0 << MPU600_GYRO_CONFIG_YG_ST) |
+                            (0 << MPU600_GYRO_CONFIG_ZG_ST) |
+                            (MPU600_GYRO_CONFIG_FS_SEL_2000 << MPU600_GYRO_CONFIG_FS_SEL));
+
+       ao_delay(AO_MS_TO_TICKS(10));
+       ao_mpu6000_sample(&normal_mode);
+       
+       errors += ao_mpu6000_accel_check(normal_mode.accel_x, test_mode.accel_x, "x");
+       errors += ao_mpu6000_accel_check(normal_mode.accel_y, test_mode.accel_y, "y");
+       errors += ao_mpu6000_accel_check(normal_mode.accel_z, test_mode.accel_z, "z");
+
+       errors += ao_mpu6000_gyro_check(normal_mode.gyro_x, test_mode.gyro_x, "x");
+       errors += ao_mpu6000_gyro_check(normal_mode.gyro_y, test_mode.gyro_y, "y");
+       errors += ao_mpu6000_gyro_check(normal_mode.gyro_z, test_mode.gyro_z, "z");
+
+       if (errors)
+               ao_panic(AO_PANIC_SELF_TEST_MPU6000);
+
+       /* Filter to about 100Hz, which also sets the gyro rate to 1000Hz */
+       ao_mpu6000_reg_write(MPU6000_CONFIG,
+                            (MPU6000_CONFIG_EXT_SYNC_SET_DISABLED << MPU6000_CONFIG_EXT_SYNC_SET) |
+                            (MPU6000_CONFIG_DLPF_CFG_94_98 << MPU6000_CONFIG_DLPF_CFG));
+
+       /* Set sample rate divider to sample at 200Hz (v = gyro/rate - 1) */
+       ao_mpu6000_reg_write(MPU6000_SMPRT_DIV,
+                            1000 / 200 - 1);
+       
+       ao_delay(AO_MS_TO_TICKS(100));
+       ao_mpu6000_configured = 1;
+}
+
+static void
+ao_mpu6000(void)
+{
+       ao_mpu6000_setup();
+       for (;;)
+       {
+               ao_mpu6000_sample((struct ao_mpu6000_sample *) &ao_data_ring[ao_data_head].mpu6000);
+               ao_arch_critical(
+                       AO_DATA_PRESENT(AO_DATA_MPU6000);
+                       AO_DATA_WAIT();
+                       );
+       }
+}
+
+static struct ao_task ao_mpu6000_task;
+
+static void
+ao_mpu6000_show(void)
+{
+       struct ao_data  sample;
+
+       ao_data_get(&sample);
+       printf ("Accel: %7d %7d %7d Gyro: %7d %7d %7d\n",
+               sample.mpu6000.accel_x,
+               sample.mpu6000.accel_y,
+               sample.mpu6000.accel_z,
+               sample.mpu6000.gyro_x,
+               sample.mpu6000.gyro_y,
+               sample.mpu6000.gyro_z);
+}
+
+static const struct ao_cmds ao_mpu6000_cmds[] = {
+       { ao_mpu6000_show,      "I\0Show MPU6000 status" },
+       { 0, NULL }
+};
+
+void
+ao_mpu6000_init(void)
+{
+       ao_mpu6000_configured = 0;
+
+       ao_add_task(&ao_mpu6000_task, ao_mpu6000, "mpu6000");
+       ao_cmd_register(&ao_mpu6000_cmds[0]);
+}
diff --git a/src/drivers/ao_mpu6000.h b/src/drivers/ao_mpu6000.h
new file mode 100644 (file)
index 0000000..ca76b08
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_MPU6000_H_
+#define _AO_MPU6000_H_
+
+#define MPU6000_ADDR_WRITE     0xd0
+#define MPU6000_ADDR_READ      0xd1
+
+#define MPU6000_SMPRT_DIV      0x19
+
+#define MPU6000_CONFIG         0x1a
+
+#define  MPU6000_CONFIG_EXT_SYNC_SET   3
+#define  MPU6000_CONFIG_EXT_SYNC_SET_DISABLED          0
+#define  MPU6000_CONFIG_EXT_SYNC_SET_TEMP_OUT_L                1
+#define  MPU6000_CONFIG_EXT_SYNC_SET_GYRO_XOUT_L       2
+#define  MPU6000_CONFIG_EXT_SYNC_SET_GYRO_YOUT_L       3
+#define  MPU6000_CONFIG_EXT_SYNC_SET_GYRO_ZOUT_L       4
+#define  MPU6000_CONFIG_EXT_SYNC_SET_ACCEL_XOUT_L      5
+#define  MPU6000_CONFIG_EXT_SYNC_SET_ACCEL_YOUT_L      6
+#define  MPU6000_CONFIG_EXT_SYNC_SET_ACCEL_ZOUT_L      7
+#define  MPU6000_CONFIG_EXT_SYNC_SET_MASK              7
+
+#define  MPU6000_CONFIG_DLPF_CFG       0
+#define  MPU6000_CONFIG_DLPF_CFG_260_256               0
+#define  MPU6000_CONFIG_DLPF_CFG_184_188               1
+#define  MPU6000_CONFIG_DLPF_CFG_94_98                 2
+#define  MPU6000_CONFIG_DLPF_CFG_44_42                 3
+#define  MPU6000_CONFIG_DLPF_CFG_21_20                 4
+#define  MPU6000_CONFIG_DLPF_CFG_10_10                 5
+#define  MPU6000_CONFIG_DLPF_CFG_5_5                   6
+#define  MPU6000_CONFIG_DLPF_CFG_MASK                  7
+
+#define MPU6000_GYRO_CONFIG    0x1b
+# define MPU600_GYRO_CONFIG_XG_ST      7
+# define MPU600_GYRO_CONFIG_YG_ST      6
+# define MPU600_GYRO_CONFIG_ZG_ST      5
+# define MPU600_GYRO_CONFIG_FS_SEL     3
+# define MPU600_GYRO_CONFIG_FS_SEL_250         0
+# define MPU600_GYRO_CONFIG_FS_SEL_500         1
+# define MPU600_GYRO_CONFIG_FS_SEL_1000                2
+# define MPU600_GYRO_CONFIG_FS_SEL_2000                3
+# define MPU600_GYRO_CONFIG_FS_SEL_MASK                3
+
+#define MPU6000_ACCEL_CONFIG   0x1c
+# define MPU600_ACCEL_CONFIG_XA_ST     7
+# define MPU600_ACCEL_CONFIG_YA_ST     6
+# define MPU600_ACCEL_CONFIG_ZA_ST     5
+# define MPU600_ACCEL_CONFIG_AFS_SEL   3
+# define MPU600_ACCEL_CONFIG_AFS_SEL_2G                0
+# define MPU600_ACCEL_CONFIG_AFS_SEL_4G                1
+# define MPU600_ACCEL_CONFIG_AFS_SEL_8G                2
+# define MPU600_ACCEL_CONFIG_AFS_SEL_16G       3
+# define MPU600_ACCEL_CONFIG_AFS_SEL_MASK      3
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF 0
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF_RESET   0
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF_5Hz     1
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF_2_5Hz   2
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF_1_25Hz  3
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF_0_63Hz  4
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF_HOLD    7
+# define MPU600_ACCEL_CONFIG_ACCEL_HPF_MASK    7
+
+#define MPU6000_INT_ENABLE     0x38
+#define  MPU6000_INT_ENABLE_FF_EN              7
+#define  MPU6000_INT_ENABLE_MOT_EN             6
+#define  MPU6000_INT_ENABLE_ZMOT_EN            5
+#define  MPU6000_INT_ENABLE_FIFO_OFLOW_EN      4
+#define  MPU6000_INT_ENABLE_I2C_MST_INT_EN     3
+#define  MPU6000_INT_ENABLE_DATA_RDY_EN                0
+
+#define MPU6000_INT_STATUS     0x3a
+#define  MPU6000_INT_STATUS_FF_EN              7
+#define  MPU6000_INT_STATUS_MOT_EN             6
+#define  MPU6000_INT_STATUS_ZMOT_EN            5
+#define  MPU6000_INT_STATUS_FIFO_OFLOW_EN      4
+#define  MPU6000_INT_STATUS_I2C_MST_INT_EN     3
+#define  MPU6000_INT_STATUS_DATA_RDY_EN                0
+
+#define MPU6000_ACCEL_XOUT_H           0x3b
+#define MPU6000_ACCEL_XOUT_L           0x3c
+#define MPU6000_ACCEL_YOUT_H           0x3d
+#define MPU6000_ACCEL_YOUT_L           0x3e
+#define MPU6000_ACCEL_ZOUT_H           0x3f
+#define MPU6000_ACCEL_ZOUT_L           0x40
+#define MPU6000_TEMP_H                 0x41
+#define MPU6000_TEMP_L                 0x42
+#define MPU6000_GYRO_XOUT_H            0x43
+#define MPU6000_GYRO_XOUT_L            0x44
+#define MPU6000_GYRO_YOUT_H            0x45
+#define MPU6000_GYRO_YOUT_L            0x46
+#define MPU6000_GYRO_ZOUT_H            0x47
+#define MPU6000_GYRO_ZOUT_L            0x48
+
+#define MPU6000_SIGNAL_PATH_RESET      0x68
+#define MPU6000_SIGNAL_PATH_RESET_GYRO_RESET   2
+#define MPU6000_SIGNAL_PATH_RESET_ACCEL_RESET  1
+#define MPU6000_SIGNAL_PATH_RESET_TEMP_RESET   0
+
+#define MPU6000_USER_CONTROL           0x6a
+#define MPU6000_USER_CONTROL_FIFO_EN           6
+#define MPU6000_USER_CONTROL_I2C_MST_EN                5
+#define MPU6000_USER_CONTROL_I2C_IF_DIS                4
+#define MPU6000_USER_CONTROL_FIFO_RESET                2
+#define MPU6000_USER_CONTROL_I2C_MST_RESET     1
+#define MPU6000_USER_CONTROL_SIG_COND_RESET    0
+
+#define MPU6000_PWR_MGMT_1     0x6b
+#define MPU6000_PWR_MGMT_1_DEVICE_RESET                7
+#define MPU6000_PWR_MGMT_1_SLEEP               6
+#define MPU6000_PWR_MGMT_1_CYCLE               5
+#define MPU6000_PWR_MGMT_1_TEMP_DIS            3
+#define MPU6000_PWR_MGMT_1_CLKSEL              0
+#define MPU6000_PWR_MGMT_1_CLKSEL_INTERNAL             0
+#define MPU6000_PWR_MGMT_1_CLKSEL_PLL_X_AXIS           1
+#define MPU6000_PWR_MGMT_1_CLKSEL_PLL_Y_AXIS           2
+#define MPU6000_PWR_MGMT_1_CLKSEL_PLL_Z_AXIS           3
+#define MPU6000_PWR_MGMT_1_CLKSEL_PLL_EXTERNAL_32K     4
+#define MPU6000_PWR_MGMT_1_CLKSEL_PLL_EXTERNAL_19M     5
+#define MPU6000_PWR_MGMT_1_CLKSEL_STOP                 7
+#define MPU6000_PWR_MGMT_1_CLKSEL_MASK                 7
+
+#define MPU6000_PWR_MGMT_2     0x6c
+
+#define MPU6000_WHO_AM_I       0x75
+
+/* Self test acceleration is approximately 0.5g */
+#define MPU6000_ST_ACCEL(full_scale)   (32767 / ((full_scale) * 2))
+
+/* Self test gyro is approximately 50°/s */
+#define MPU6000_ST_GYRO(full_scale)    ((int16_t) (((int32_t) 32767 * (int32_t) 50) / (full_scale)))
+
+struct ao_mpu6000_sample {
+       int16_t         accel_x;
+       int16_t         accel_y;
+       int16_t         accel_z;
+       int16_t         temp;
+       int16_t         gyro_x;
+       int16_t         gyro_y;
+       int16_t         gyro_z;
+};
+
+void
+ao_mpu6000_init(void);
+
+#endif /* _AO_MPU6000_H_ */
diff --git a/src/drivers/ao_ms5607.c b/src/drivers/ao_ms5607.c
new file mode 100644 (file)
index 0000000..704b458
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+#include "ao_ms5607.h"
+
+#if HAS_MS5607
+
+static struct ao_ms5607_prom   ms5607_prom;
+static uint8_t                 ms5607_configured;
+
+static void
+ao_ms5607_start(void) {
+       ao_spi_get(AO_MS5607_SPI_INDEX,AO_SPI_SPEED_FAST);
+       stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 0);
+}
+
+static void
+ao_ms5607_stop(void) {
+       stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1);
+       ao_spi_put(AO_MS5607_SPI_INDEX);
+}
+
+static void
+ao_ms5607_reset(void) {
+       uint8_t cmd;
+
+       cmd = AO_MS5607_RESET;
+       ao_ms5607_start();
+       ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX);
+       ao_delay(AO_MS_TO_TICKS(10));
+       ao_ms5607_stop();
+}
+
+static uint8_t
+ao_ms5607_crc(uint8_t *prom)
+{
+       uint8_t         crc_byte = prom[15];
+       uint8_t         cnt;
+       uint16_t        n_rem = 0;
+       uint16_t        crc_read;
+       uint8_t         n_bit;
+
+       prom[15] = 0;
+       for (cnt = 0; cnt < 16; cnt++) {
+               n_rem ^= prom[cnt];
+               for (n_bit = 8; n_bit > 0; n_bit--) {
+                       if (n_rem & 0x8000)
+                               n_rem = (n_rem << 1) ^ 0x3000;
+                       else
+                               n_rem = (n_rem << 1);
+               }
+       }
+       n_rem = (n_rem >> 12) & 0xf;
+       prom[15] = crc_byte;
+       return n_rem;
+}
+
+static void
+ao_ms5607_prom_read(struct ao_ms5607_prom *prom)
+{
+       uint8_t         addr;
+       uint8_t         crc;
+       uint16_t        *r;
+
+       r = (uint16_t *) prom;
+       for (addr = 0; addr < 8; addr++) {
+               uint8_t cmd = AO_MS5607_PROM_READ(addr);
+               ao_ms5607_start();
+               ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX);
+               ao_spi_recv(r, 2, AO_MS5607_SPI_INDEX);
+               ao_ms5607_stop();
+               r++;
+       }
+       crc = ao_ms5607_crc((uint8_t *) prom);
+       if (crc != (((uint8_t *) prom)[15] & 0xf)) {
+               printf ("MS5607 PROM CRC error (computed %x actual %x)\n",
+                       crc, (((uint8_t *) prom)[15] & 0xf));
+               flush();
+//             ao_panic(AO_PANIC_SELF_TEST_MS5607);
+       }
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       /* Byte swap */
+       r = (uint16_t *) prom;
+       for (addr = 0; addr < 8; addr++) {
+               uint16_t        t = *r;
+               *r++ = (t << 8) | (t >> 8);
+       }
+#endif
+}
+
+static void
+ao_ms5607_setup(void)
+{
+       if (ms5607_configured)
+               return;
+       ms5607_configured = 1;
+       ao_ms5607_reset();
+       ao_ms5607_prom_read(&ms5607_prom);
+}
+
+static uint8_t ao_ms5607_done;
+
+static void
+ao_ms5607_isr(void)
+{
+       ao_exti_disable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
+       ao_ms5607_done = 1;
+       ao_wakeup(&ao_ms5607_done);
+}
+
+static uint32_t
+ao_ms5607_get_sample(uint8_t cmd) {
+       uint8_t reply[3];
+       uint8_t read;
+       uint16_t now;
+
+       ao_ms5607_done = 0;
+
+       ao_ms5607_start();
+       ao_spi_send(&cmd, 1, AO_MS5607_SPI_INDEX);
+       ao_exti_enable(AO_MS5607_MISO_PORT, AO_MS5607_MISO_PIN);
+#if AO_MS5607_PRIVATE_PINS
+       ao_spi_put(AO_MS5607_SPI_INDEX);
+#endif
+       cli();
+       while (!ao_ms5607_done)
+               ao_sleep(&ao_ms5607_done);
+       sei();
+#if AO_MS5607_PRIVATE_PINS
+       stm_gpio_set(AO_MS5607_CS_PORT, AO_MS5607_CS_PIN, 1);
+#else
+       ao_ms5607_stop();
+#endif
+
+       ao_ms5607_start();
+       read = AO_MS5607_ADC_READ;
+       ao_spi_send(&read, 1, AO_MS5607_SPI_INDEX);
+       ao_spi_recv(&reply, 3, AO_MS5607_SPI_INDEX);
+       ao_ms5607_stop();
+
+       return ((uint32_t) reply[0] << 16) | ((uint32_t) reply[1] << 8) | (uint32_t) reply[2];
+}
+
+void
+ao_ms5607_sample(struct ao_ms5607_sample *sample)
+{
+       sample->pres = ao_ms5607_get_sample(AO_MS5607_CONVERT_D1_2048);
+       sample->temp = ao_ms5607_get_sample(AO_MS5607_CONVERT_D2_2048);
+}
+
+void
+ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value)
+{
+       uint8_t addr;
+       int32_t dT;
+       int32_t TEMP;
+       int64_t OFF;
+       int64_t SENS;
+       int32_t P;
+
+       dT = sample->temp - ((int32_t) ms5607_prom.tref << 8);
+       
+       TEMP = 2000 + (((int64_t) dT * ms5607_prom.tempsens) >> 23);
+
+       OFF = ((int64_t) ms5607_prom.off << 17) + (((int64_t) ms5607_prom.tco * dT) >> 6);
+
+       SENS = ((int64_t) ms5607_prom.sens << 16) + (((int64_t) ms5607_prom.tcs * dT) >> 7);
+
+       if (TEMP < 2000) {
+               int32_t T2 = ((int64_t) dT * (int64_t) dT) >> 31;
+               int32_t TEMPM = TEMP - 2000;
+               int64_t OFF2 = (61 * (int64_t) TEMPM * (int64_t) TEMPM) >> 4;
+               int64_t SENS2 = 2 * (int64_t) TEMPM * (int64_t) TEMPM;
+               if (TEMP < 1500) {
+                       int32_t TEMPP = TEMP + 1500;
+                       int64_t TEMPP2 = TEMPP * TEMPP;
+                       OFF2 = OFF2 + 15 * TEMPP2;
+                       SENS2 = SENS2 + 8 * TEMPP2;
+               }
+               TEMP -= T2;
+               OFF -= OFF2;
+               SENS -= SENS2;
+       }
+
+       value->pres = ((((int64_t) sample->pres * SENS) >> 21) - OFF) >> 15;
+       value->temp = TEMP;
+}
+
+static void
+ao_ms5607(void)
+{
+       ao_ms5607_setup();
+       for (;;)
+       {
+               ao_ms5607_sample((struct ao_ms5607_sample *) &ao_data_ring[ao_data_head].ms5607_raw);
+               ao_arch_critical(
+                       AO_DATA_PRESENT(AO_DATA_MS5607);
+                       AO_DATA_WAIT();
+                       );
+       }
+}
+
+__xdata struct ao_task ao_ms5607_task;
+
+void
+ao_ms5607_info(void)
+{
+       printf ("ms5607 reserved: %u\n", ms5607_prom.reserved);
+       printf ("ms5607 sens: %u\n", ms5607_prom.sens);
+       printf ("ms5607 off: %u\n", ms5607_prom.off);
+       printf ("ms5607 tcs: %u\n", ms5607_prom.tcs);
+       printf ("ms5607 tco: %u\n", ms5607_prom.tco);
+       printf ("ms5607 tref: %u\n", ms5607_prom.tref);
+       printf ("ms5607 tempsens: %u\n", ms5607_prom.tempsens);
+       printf ("ms5607 crc: %u\n", ms5607_prom.crc);
+}
+
+static void
+ao_ms5607_dump(void)
+{
+       struct ao_ms5607_sample sample;
+       struct ao_ms5607_value value;
+
+       ao_ms5607_setup();
+       ao_ms5607_sample(&sample);
+       ao_ms5607_convert(&sample, &value);
+       printf ("Pressure:    %8u %8d\n", sample.pres, value.pres);
+       printf ("Temperature: %8u %8d\n", sample.temp, value.temp);
+       printf ("Altitude: %ld\n", ao_pa_to_altitude(value.pres));
+}
+
+__code struct ao_cmds ao_ms5607_cmds[] = {
+       { ao_ms5607_dump,       "B\0Display MS5607 data" },
+       { 0, NULL },
+};
+
+void
+ao_ms5607_init(void)
+{
+       ms5607_configured = 0;
+       ao_cmd_register(&ao_ms5607_cmds[0]);
+       ao_spi_init_cs(AO_MS5607_CS_PORT, (1 << AO_MS5607_CS_PIN));
+
+//     ao_add_task(&ao_ms5607_task, ao_ms5607, "ms5607");
+
+       /* Configure the MISO pin as an interrupt; when the
+        * conversion is complete, the MS5607 will raise this
+        * pin as a signal
+        */
+       ao_exti_setup(AO_MS5607_MISO_PORT,
+                     AO_MS5607_MISO_PIN,
+                     AO_EXTI_MODE_RISING,
+                     ao_ms5607_isr);
+
+       /* Reset the pin from INPUT to ALTERNATE so that SPI works
+        * This needs an abstraction at some point...
+        */
+       stm_moder_set(AO_MS5607_MISO_PORT,
+                     AO_MS5607_MISO_PIN,
+                     STM_MODER_ALTERNATE);
+}
+
+#endif
diff --git a/src/drivers/ao_ms5607.h b/src/drivers/ao_ms5607.h
new file mode 100644 (file)
index 0000000..e9c364d
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_MS5607_H_
+#define _AO_MS5607_H_
+
+#define AO_MS5607_RESET                        0x1e
+
+#define AO_MS5607_CONVERT_D1_256       0x40
+#define AO_MS5607_CONVERT_D1_512       0x42
+#define AO_MS5607_CONVERT_D1_1024      0x44
+#define AO_MS5607_CONVERT_D1_2048      0x46
+#define AO_MS5607_CONVERT_D1_4096      0x48
+
+#define AO_MS5607_CONVERT_D2_256       0x50
+#define AO_MS5607_CONVERT_D2_512       0x52
+#define AO_MS5607_CONVERT_D2_1024      0x54
+#define AO_MS5607_CONVERT_D2_2048      0x56
+#define AO_MS5607_CONVERT_D2_4096      0x58
+
+#define AO_MS5607_ADC_READ             0x00
+#define AO_MS5607_PROM_READ(ad)                (0xA0 | ((ad) << 1))
+
+struct ao_ms5607_prom {
+       uint16_t        reserved;
+       uint16_t        sens;
+       uint16_t        off;
+       uint16_t        tcs;
+       uint16_t        tco;
+       uint16_t        tref;
+       uint16_t        tempsens;
+       uint16_t        crc;
+};
+
+struct ao_ms5607_sample {
+       uint32_t        pres;   /* raw 24 bit sensor */
+       uint32_t        temp;   /* raw 24 bit sensor */
+};
+
+struct ao_ms5607_value {
+       int32_t         pres;   /* in Pa * 10 */
+       int32_t         temp;   /* in °C * 100 */
+};
+
+void
+ao_ms5607_init(void);
+
+void
+ao_ms5607_info(void);
+
+void
+ao_ms5607_sample(struct ao_ms5607_sample *sample);
+
+void
+ao_ms5607_convert(struct ao_ms5607_sample *sample, struct ao_ms5607_value *value);
+
+void
+ao_ms5607_get_prom(struct ao_ms5607_prom *prom);
+
+#endif /* _AO_MS5607_H_ */
diff --git a/src/drivers/ao_packet.c b/src/drivers/ao_packet.c
new file mode 100644 (file)
index 0000000..d813b25
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+__xdata struct ao_packet_recv ao_rx_packet;
+__xdata struct ao_packet ao_tx_packet;
+__pdata uint8_t ao_packet_rx_len, ao_packet_rx_used, ao_packet_tx_used;
+
+static __xdata char tx_data[AO_PACKET_MAX];
+static __xdata char rx_data[AO_PACKET_MAX];
+static __pdata uint8_t rx_seq;
+
+__xdata struct ao_task ao_packet_task;
+__xdata uint8_t ao_packet_enable;
+
+#if PACKET_HAS_MASTER
+__xdata uint8_t ao_packet_master_sleeping;
+__xdata uint8_t ao_packet_last_rssi;
+#endif
+
+void
+ao_packet_send(void)
+{
+#ifdef AO_LED_RED
+       ao_led_on(AO_LED_RED);
+#endif
+       /* If any tx data is pending then copy it into the tx packet */
+       if (ao_packet_tx_used && ao_tx_packet.len == 0) {
+               ao_xmemcpy(&ao_tx_packet.d, tx_data, ao_packet_tx_used);
+               ao_tx_packet.len = ao_packet_tx_used;
+               ao_tx_packet.seq++;
+               ao_packet_tx_used = 0;
+               ao_wakeup(&tx_data);
+       }
+       ao_radio_send(&ao_tx_packet, sizeof (ao_tx_packet));
+#ifdef AO_LED_RED
+       ao_led_off(AO_LED_RED);
+#endif
+}
+
+uint8_t
+ao_packet_recv(void)
+{
+       uint8_t dma_done;
+
+#ifdef AO_LED_GREEN
+       ao_led_on(AO_LED_GREEN);
+#endif
+       dma_done = ao_radio_recv(&ao_rx_packet, sizeof (struct ao_packet_recv));
+#ifdef AO_LED_GREEN
+       ao_led_off(AO_LED_GREEN);
+#endif
+#if AO_PROFILE
+               {
+                       extern uint32_t ao_rx_start_tick, ao_rx_packet_tick, ao_rx_done_tick, ao_rx_last_done_tick;
+                       extern uint32_t ao_fec_decode_start, ao_fec_decode_end;
+
+                       printf ("between packet: %d\n", ao_rx_start_tick - ao_rx_last_done_tick);
+                       printf ("receive start delay: %d\n", ao_rx_packet_tick - ao_rx_start_tick);
+                       printf ("decode time: %d\n", ao_fec_decode_end - ao_fec_decode_start);
+                       printf ("rx cleanup: %d\n\n", ao_rx_done_tick - ao_fec_decode_end);
+                       flush();
+               }
+#endif
+
+       /* Check to see if we got a valid packet */
+       if (!dma_done)
+               return 0;
+       if (!(ao_rx_packet.status & AO_RADIO_STATUS_CRC_OK))
+               return 0;
+
+#if PACKET_HAS_MASTER
+       ao_packet_last_rssi = ao_rx_packet.rssi;
+#endif
+       /* Accept packets with matching call signs, or any packet if
+        * our callsign hasn't been configured
+        */
+       if (ao_xmemcmp(ao_rx_packet.packet.callsign,
+                      ao_config.callsign,
+                      AO_MAX_CALLSIGN) != 0 &&
+           ao_xmemcmp(ao_config.callsign, CODE_TO_XDATA("N0CALL"), 7) != 0)
+               return 0;
+
+       /* SYN packets carry no data */
+       if (ao_rx_packet.packet.len == AO_PACKET_SYN) {
+               rx_seq = ao_rx_packet.packet.seq;
+               ao_tx_packet.seq = ao_rx_packet.packet.ack;
+               ao_tx_packet.ack = rx_seq;
+       } else if (ao_rx_packet.packet.len) {
+
+               /* Check for incoming data at the next sequence and
+                * for an empty data buffer
+                */
+               if (ao_rx_packet.packet.seq == (uint8_t) (rx_seq + (uint8_t) 1) &&
+                   ao_packet_rx_used == ao_packet_rx_len) {
+
+                       /* Copy data to the receive data buffer and set up the
+                        * offsets
+                        */
+                       ao_xmemcpy(rx_data, ao_rx_packet.packet.d, ao_rx_packet.packet.len);
+                       ao_packet_rx_used = 0;
+                       ao_packet_rx_len = ao_rx_packet.packet.len;
+
+                       /* Mark the sequence that we've received to
+                        * let the sender know when we return a packet
+                        */
+                       rx_seq = ao_rx_packet.packet.seq;
+                       ao_tx_packet.ack = rx_seq;
+
+                       /* Poke anyone looking for received data */
+                       ao_wakeup(&ao_stdin_ready);
+               }
+       }
+
+       /* If the other side has seen the latest data we queued,
+        * wake up any task waiting to send data and let them go again
+        */
+       if (ao_rx_packet.packet.ack == ao_tx_packet.seq) {
+               ao_tx_packet.len = 0;
+               ao_wakeup(&ao_tx_packet);
+       }
+       return 1;
+}
+
+#if PACKET_HAS_MASTER
+void
+ao_packet_flush(void)
+{
+       /* If there is data to send, and this is the master,
+        * then poke the master to send all queued data
+        */
+       if (ao_packet_tx_used && ao_packet_master_sleeping)
+               ao_wakeup(&ao_packet_master_sleeping);
+}
+#endif /* PACKET_HAS_MASTER */
+
+void
+ao_packet_putchar(char c) __reentrant
+{
+       while (ao_packet_tx_used == AO_PACKET_MAX && ao_packet_enable) {
+#if PACKET_HAS_MASTER
+               ao_packet_flush();
+#endif
+               ao_sleep(&tx_data);
+       }
+
+       if (ao_packet_enable)
+               tx_data[ao_packet_tx_used++] = c;
+}
+
+char
+ao_packet_pollchar(void) __critical
+{
+       if (!ao_packet_enable)
+               return AO_READ_AGAIN;
+
+       if (ao_packet_rx_used == ao_packet_rx_len)
+               return AO_READ_AGAIN;
+
+       return rx_data[ao_packet_rx_used++];
+}
diff --git a/src/drivers/ao_packet_master.c b/src/drivers/ao_packet_master.c
new file mode 100644 (file)
index 0000000..e97a664
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static char
+ao_packet_getchar(void) __critical
+{
+       char c;
+       while ((c = ao_packet_pollchar()) == AO_READ_AGAIN) {
+               if (!ao_packet_enable)
+                       break;
+               if (ao_packet_master_sleeping)
+                       ao_wakeup(&ao_packet_master_sleeping);
+               flush();
+               ao_sleep(&ao_stdin_ready);
+       }
+       return c;
+}
+
+static void
+ao_packet_echo(void) __reentrant
+{
+       char    c;
+       while (ao_packet_enable) {
+               c = ao_packet_getchar();
+               if (c != AO_READ_AGAIN)
+                       putchar(c);
+       }
+       ao_exit();
+}
+
+static __xdata struct ao_task  ao_packet_echo_task;
+static __xdata uint16_t                ao_packet_master_delay;
+static __xdata uint16_t                ao_packet_master_time;
+
+#define AO_PACKET_MASTER_DELAY_SHORT   AO_MS_TO_TICKS(100)
+#define AO_PACKET_MASTER_DELAY_LONG    AO_MS_TO_TICKS(1000)
+#define AO_PACKET_MASTER_DELAY_TIMEOUT AO_MS_TO_TICKS(2000)
+
+static void
+ao_packet_master_busy(void)
+{
+       ao_packet_master_delay = AO_PACKET_MASTER_DELAY_SHORT;
+       ao_packet_master_time = ao_time();
+}
+
+static void
+ao_packet_master_check_busy(void)
+{
+       int16_t idle;
+       if (ao_packet_master_delay != AO_PACKET_MASTER_DELAY_SHORT)
+               return;
+       idle = (int16_t) (ao_time() - ao_packet_master_time);
+
+       if (idle > AO_PACKET_MASTER_DELAY_TIMEOUT)
+               ao_packet_master_delay = AO_PACKET_MASTER_DELAY_LONG;
+}
+
+void
+ao_packet_master(void)
+{
+       ao_config_get();
+       ao_tx_packet.addr = ao_serial_number;
+       ao_tx_packet.len = AO_PACKET_SYN;
+       ao_packet_master_time = ao_time();
+       ao_packet_master_delay = AO_PACKET_MASTER_DELAY_SHORT;
+       while (ao_packet_enable) {
+               uint8_t r;
+               ao_xmemcpy(ao_tx_packet.callsign, ao_config.callsign, AO_MAX_CALLSIGN);
+               ao_packet_send();
+               if (ao_tx_packet.len)
+                       ao_packet_master_busy();
+               ao_packet_master_check_busy();
+               ao_alarm(ao_packet_master_delay);
+               r = ao_packet_recv();
+               ao_clear_alarm();
+               if (r) {
+                       /* if we can transmit data, do so */
+                       if (ao_packet_tx_used && ao_tx_packet.len == 0)
+                               continue;
+                       if (ao_rx_packet.packet.len)
+                               ao_packet_master_busy();
+                       ao_packet_master_sleeping = 1;
+                       ao_alarm(ao_packet_master_delay);
+                       ao_sleep(&ao_packet_master_sleeping);
+                       ao_clear_alarm();
+                       ao_packet_master_sleeping = 0;
+               }
+       }
+       ao_exit();
+}
+
+static void
+ao_packet_forward(void) __reentrant
+{
+       char c;
+       ao_packet_enable = 1;
+       ao_cmd_white();
+
+       flush();
+#if HAS_MONITOR
+       ao_monitor_disable();
+#endif
+       ao_add_task(&ao_packet_task, ao_packet_master, "master");
+       ao_add_task(&ao_packet_echo_task, ao_packet_echo, "echo");
+       while ((c = getchar()) != '~') {
+               if (c == '\r') c = '\n';
+               ao_packet_putchar(c);
+       }
+
+       /* Wait for a second if there is any pending data */
+       for (c = 0; (ao_packet_tx_used || ao_tx_packet.len) && c < 10; c++)
+               ao_delay(AO_MS_TO_TICKS(100));
+       ao_packet_enable = 0;
+       while (ao_packet_echo_task.wchan || ao_packet_task.wchan) {
+               ao_radio_recv_abort();
+               ao_wakeup(&ao_stdin_ready);
+               ao_delay(AO_MS_TO_TICKS(10));
+       }
+#if HAS_MONITOR
+       ao_monitor_enable();
+#endif
+}
+
+static void
+ao_packet_signal(void)
+{
+       printf ("RSSI: %d\n", AO_RSSI_FROM_RADIO(ao_packet_last_rssi));
+}
+
+__code struct ao_cmds ao_packet_master_cmds[] = {
+       { ao_packet_forward,    "p\0Remote packet link." },
+       { ao_packet_signal,     "s\0Report signal strength." },
+       { 0,    NULL },
+};
+
+void
+ao_packet_master_init(void)
+{
+       ao_cmd_register(&ao_packet_master_cmds[0]);
+}
diff --git a/src/drivers/ao_packet_slave.c b/src/drivers/ao_packet_slave.c
new file mode 100644 (file)
index 0000000..fd5d443
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+ao_packet_slave(void)
+{
+       ao_tx_packet.addr = ao_serial_number;
+       ao_tx_packet.len = AO_PACKET_SYN;
+       while (ao_packet_enable) {
+               if (ao_packet_recv()) {
+                       ao_xmemcpy(&ao_tx_packet.callsign, &ao_rx_packet.packet.callsign, AO_MAX_CALLSIGN);
+#if HAS_FLIGHT
+                       ao_flight_force_idle = TRUE;
+#endif
+                       ao_packet_send();
+               }
+       }
+       ao_exit();
+}
+
+void
+ao_packet_slave_start(void)
+{
+       if (!ao_packet_enable) {
+               ao_packet_enable = 1;
+               ao_add_task(&ao_packet_task, ao_packet_slave, "slave");
+       }
+}
+
+void
+ao_packet_slave_stop(void)
+{
+       if (ao_packet_enable) {
+               ao_packet_enable = 0;
+               while (ao_packet_task.wchan) {
+                       ao_radio_recv_abort();
+                       ao_delay(AO_MS_TO_TICKS(10));
+               }
+       }
+}
+
+void
+ao_packet_slave_init(uint8_t enable)
+{
+       ao_add_stdio(ao_packet_pollchar,
+                    ao_packet_putchar,
+                    NULL);
+       if (enable)
+               ao_packet_slave_start();
+}
diff --git a/src/drivers/ao_pad.c b/src/drivers/ao_pad.c
new file mode 100644 (file)
index 0000000..55e6289
--- /dev/null
@@ -0,0 +1,356 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_pad.h>
+#include <ao_74hc497.h>
+#include <ao_radio_cmac.h>
+
+static __xdata uint8_t ao_pad_ignite;
+static __xdata struct ao_pad_command   command;
+static __xdata struct ao_pad_query     query;
+static __pdata uint8_t ao_pad_armed;
+static __pdata uint16_t        ao_pad_arm_time;
+static __pdata uint8_t ao_pad_box;
+static __xdata uint8_t ao_pad_disabled;
+
+#define DEBUG  1
+
+#if DEBUG
+static __pdata uint8_t ao_pad_debug;
+#define PRINTD(...) (ao_pad_debug ? (printf(__VA_ARGS__), 0) : 0)
+#define FLUSHD()    (ao_pad_debug ? (flush(), 0) : 0)
+#else
+#define PRINTD(...) 
+#define FLUSHD()    
+#endif
+
+static void
+ao_pad_run(void)
+{
+       for (;;) {
+               while (!ao_pad_ignite)
+                       ao_sleep(&ao_pad_ignite);
+               /*
+                * Actually set the pad bits
+                */
+               AO_PAD_PORT = (AO_PAD_PORT & (~AO_PAD_ALL_PINS)) | ao_pad_ignite;
+               while (ao_pad_ignite) {
+                       ao_pad_ignite = 0;
+                       ao_delay(AO_PAD_FIRE_TIME);
+               }
+               AO_PAD_PORT &= ~(AO_PAD_ALL_PINS);
+       }
+}
+
+#define AO_PAD_ARM_BEEP_INTERVAL       200
+
+static void
+ao_pad_monitor(void)
+{
+       uint8_t                 c;
+       uint8_t                 sample;
+       __pdata uint8_t         prev = 0, cur = 0;
+       __pdata uint8_t         beeping = 0;
+       __xdata struct ao_data  *packet;
+       __pdata uint16_t        arm_beep_time = 0;
+
+       sample = ao_data_head;
+       for (;;) {
+               __pdata int16_t                 pyro;
+               ao_arch_critical(
+                       while (sample == ao_data_head)
+                               ao_sleep((void *) DATA_TO_XDATA(&ao_data_head));
+                       );
+
+               packet = &ao_data_ring[sample];
+               sample = ao_data_ring_next(sample);
+
+               pyro = packet->adc.pyro;
+
+#define VOLTS_TO_PYRO(x) ((int16_t) ((x) * 27.0 / 127.0 / 3.3 * 32767.0))
+
+               cur = 0;
+               if (pyro > VOLTS_TO_PYRO(10)) {
+                       query.arm_status = AO_PAD_ARM_STATUS_ARMED;
+                       cur |= AO_LED_ARMED;
+               } else if (pyro < VOLTS_TO_PYRO(5)) {
+                       query.arm_status = AO_PAD_ARM_STATUS_DISARMED;
+                       arm_beep_time = 0;
+               } else {
+                       if ((ao_time() % 100) < 50)
+                               cur |= AO_LED_ARMED;
+                       query.arm_status = AO_PAD_ARM_STATUS_UNKNOWN;
+                       arm_beep_time = 0;
+               }
+
+               for (c = 0; c < AO_PAD_NUM; c++) {
+                       int16_t         sense = packet->adc.sense[c];
+                       uint8_t status = AO_PAD_IGNITER_STATUS_UNKNOWN;
+
+                       /*
+                        *      pyro is run through a divider, so pyro = v_pyro * 27 / 127 ~= v_pyro / 20
+                        *      v_pyro = pyro * 127 / 27
+                        *
+                        *              v_pyro \
+                        *      100k            igniter
+                        *              output /        
+                        *      100k           \
+                        *              sense   relay
+                        *      27k            / 
+                        *              gnd ---   
+                        *
+                        *      If the relay is closed, then sense will be 0
+                        *      If no igniter is present, then sense will be v_pyro * 27k/227k = pyro * 127 / 227 ~= pyro/2
+                        *      If igniter is present, then sense will be v_pyro * 27k/127k ~= v_pyro / 20 = pyro
+                        */
+
+                       if (sense <= pyro / 8) {
+                               status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_CLOSED;
+                               if ((ao_time() % 100) < 50)
+                                       cur |= AO_LED_CONTINUITY(c);
+                       }
+                       else if (pyro / 8 * 3 <= sense && sense <= pyro / 8 * 5)
+                               status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
+                       else if (pyro / 8 * 7 <= sense) {
+                               status = AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN;
+                               cur |= AO_LED_CONTINUITY(c);
+                       }
+                       query.igniter_status[c] = status;
+               }
+               if (cur != prev) {
+                       PRINTD("change leds from %02x to %02x mask %02x\n",
+                              prev, cur, AO_LED_CONTINUITY_MASK|AO_LED_ARMED);
+                       ao_led_set_mask(cur, AO_LED_CONTINUITY_MASK | AO_LED_ARMED);
+                       prev = cur;
+               }
+
+               if (ao_pad_armed) {
+                       if (sample & 2)
+                               ao_beep(AO_BEEP_HIGH);
+                       else
+                               ao_beep(AO_BEEP_LOW);
+                       beeping = 1;
+               } else if (query.arm_status == AO_PAD_ARM_STATUS_ARMED && !beeping) {
+                       if (arm_beep_time == 0) {
+                               arm_beep_time = AO_PAD_ARM_BEEP_INTERVAL;
+                               beeping = 1;
+                               ao_beep(AO_BEEP_HIGH);
+                       }
+                       --arm_beep_time;
+               } else if (beeping) {
+                       beeping = 0;
+                       ao_beep(0);
+               }
+       }
+}
+
+void
+ao_pad_disable(void)
+{
+       if (!ao_pad_disabled) {
+               ao_pad_disabled = 1;
+               ao_radio_recv_abort();
+       }
+}
+
+void
+ao_pad_enable(void)
+{
+       ao_pad_disabled = 0;
+       ao_wakeup (&ao_pad_disabled);
+}
+
+static void
+ao_pad(void)
+{
+       int16_t time_difference;
+       int8_t  ret;
+
+       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+       ao_pad_box = 0;
+       ao_led_set(0);
+       ao_led_on(AO_LED_POWER);
+       for (;;) {
+               FLUSHD();
+               while (ao_pad_disabled)
+                       ao_sleep(&ao_pad_disabled);
+               ret = ao_radio_cmac_recv(&command, sizeof (command), 0);
+               PRINTD ("cmac_recv %d\n", ret);
+               if (ret != AO_RADIO_CMAC_OK)
+                       continue;
+               
+               PRINTD ("tick %d box %d cmd %d channels %02x\n",
+                       command.tick, command.box, command.cmd, command.channels);
+
+               if (ao_pad_armed && (int16_t) (ao_time() - ao_pad_arm_time) > AO_PAD_ARM_TIME)
+                       ao_pad_armed = 0;
+
+               switch (command.cmd) {
+               case AO_LAUNCH_ARM:
+                       if (command.box != ao_pad_box) {
+                               PRINTD ("box number mismatch\n");
+                               break;
+                       }
+
+                       if (command.channels & ~(AO_PAD_ALL_PINS))
+                               break;
+
+                       time_difference = command.tick - ao_time();
+                       PRINTD ("arm tick %d local tick %d\n", command.tick, ao_time());
+                       if (time_difference < 0)
+                               time_difference = -time_difference;
+                       if (time_difference > 10) {
+                               PRINTD ("time difference too large %d\n", time_difference);
+                               break;
+                       }
+                       PRINTD ("armed\n");
+                       ao_pad_armed = command.channels;
+                       ao_pad_arm_time = ao_time();
+
+                       /* fall through ... */
+
+               case AO_LAUNCH_QUERY:
+                       if (command.box != ao_pad_box) {
+                               PRINTD ("box number mismatch\n");
+                               break;
+                       }
+
+                       query.tick = ao_time();
+                       query.box = ao_pad_box;
+                       query.channels = AO_PAD_ALL_PINS;
+                       query.armed = ao_pad_armed;
+                       PRINTD ("query tick %d box %d channels %02x arm %d arm_status %d igniter %d,%d,%d,%d\n",
+                               query.tick, query.box, query.channels, query.armed,
+                               query.arm_status,
+                               query.igniter_status[0],
+                               query.igniter_status[1],
+                               query.igniter_status[2],
+                               query.igniter_status[3]);
+                       ao_radio_cmac_send(&query, sizeof (query));
+                       break;
+               case AO_LAUNCH_FIRE:
+                       if (!ao_pad_armed) {
+                               PRINTD ("not armed\n");
+                               break;
+                       }
+                       if ((uint16_t) (ao_time() - ao_pad_arm_time) > AO_SEC_TO_TICKS(20)) {
+                               PRINTD ("late pad arm_time %d time %d\n",
+                                       ao_pad_arm_time, ao_time());
+                               break;
+                       }
+                       time_difference = command.tick - ao_time();
+                       if (time_difference < 0)
+                               time_difference = -time_difference;
+                       if (time_difference > 10) {
+                               PRINTD ("time different too large %d\n", time_difference);
+                               break;
+                       }
+                       PRINTD ("ignite\n");
+                       ao_pad_ignite = ao_pad_armed;
+                       ao_pad_arm_time = ao_time();
+                       ao_wakeup(&ao_pad_ignite);
+                       break;
+               }
+       }
+}
+
+void
+ao_pad_test(void)
+{
+       uint8_t c;
+
+       printf ("Arm switch: ");
+       switch (query.arm_status) {
+       case AO_PAD_ARM_STATUS_ARMED:
+               printf ("Armed\n");
+               break;
+       case AO_PAD_ARM_STATUS_DISARMED:
+               printf ("Disarmed\n");
+               break;
+       case AO_PAD_ARM_STATUS_UNKNOWN:
+               printf ("Unknown\n");
+               break;
+       }
+
+       for (c = 0; c < AO_PAD_NUM; c++) {
+               printf ("Pad %d: ");
+               switch (query.igniter_status[c]) {
+               case AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_CLOSED:     printf ("No igniter. Relay closed\n"); break;
+               case AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN:       printf ("No igniter. Relay open\n"); break;
+               case AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN:     printf ("Good igniter. Relay open\n"); break;
+               case AO_PAD_IGNITER_STATUS_UNKNOWN:                     printf ("Unknown\n"); break;
+               }
+       }
+}
+
+void
+ao_pad_manual(void)
+{
+       ao_cmd_white();
+       if (!ao_match_word("DoIt"))
+               return;
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       ao_pad_ignite = 1 << ao_cmd_lex_i;
+       ao_wakeup(&ao_pad_ignite);
+}
+
+static __xdata struct ao_task ao_pad_task;
+static __xdata struct ao_task ao_pad_ignite_task;
+static __xdata struct ao_task ao_pad_monitor_task;
+
+#if DEBUG
+void
+ao_pad_set_debug(void)
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status == ao_cmd_success)
+               ao_pad_debug = ao_cmd_lex_i != 0;
+}
+#endif
+
+__code struct ao_cmds ao_pad_cmds[] = {
+       { ao_pad_test,  "t\0Test pad continuity" },
+       { ao_pad_manual,        "i <key> <n>\0Fire igniter. <key> is doit with D&I" },
+#if DEBUG
+       { ao_pad_set_debug,     "D <0 off, 1 on>\0Debug" },
+#endif
+       { 0, NULL }
+};
+
+void
+ao_pad_init(void)
+{
+#if AO_PAD_NUM > 0
+       ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_0, AO_PAD_0, 0);
+#endif
+#if AO_PAD_NUM > 1
+       ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_1, AO_PAD_1, 0);
+#endif
+#if AO_PAD_NUM > 2
+       ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_2, AO_PAD_2, 0);
+#endif
+#if AO_PAD_NUM > 3
+       ao_enable_output(AO_PAD_PORT, AO_PAD_PIN_3, AO_PAD_3, 0);
+#endif
+       ao_cmd_register(&ao_pad_cmds[0]);
+       ao_add_task(&ao_pad_task, ao_pad, "pad listener");
+       ao_add_task(&ao_pad_ignite_task, ao_pad_run, "pad igniter");
+       ao_add_task(&ao_pad_monitor_task, ao_pad_monitor, "pad monitor");
+}
diff --git a/src/drivers/ao_pad.h b/src/drivers/ao_pad.h
new file mode 100644 (file)
index 0000000..2306289
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PAD_H_
+#define _AO_PAD_H_
+
+#define AO_PAD_MAX_CHANNELS    8
+#define AO_PAD_MAX_BOXES       100
+
+struct ao_pad_command {
+       uint16_t        tick;
+       uint16_t        box;
+       uint8_t         cmd;
+       uint8_t         channels;
+};
+
+/* Report current telefire status.
+ */
+
+#define AO_PAD_QUERY           1
+
+struct ao_pad_query {
+       uint16_t        tick;           /* telefire tick */
+       uint16_t        box;            /* telefire box number */
+       uint8_t         channels;       /* which chanels are present */
+       uint8_t         armed;          /* which channels are armed */
+       uint8_t         arm_status;     /* status of arming switch */
+       uint8_t         igniter_status[AO_PAD_MAX_CHANNELS];    /* status for each igniter */
+};
+
+/* Arm pads for 3 seconds, no report
+ */
+#define AO_PAD_ARM             2
+
+#define AO_PAD_ARM_TIME                AO_SEC_TO_TICKS(3)
+
+/* Fire current armed pads for 200ms, no report
+ */
+#define AO_PAD_FIRE            3
+
+#define AO_PAD_FIRE_TIME       AO_MS_TO_TICKS(200)
+
+#define AO_PAD_ARM_STATUS_DISARMED     0
+#define AO_PAD_ARM_STATUS_ARMED                1
+#define AO_PAD_ARM_STATUS_UNKNOWN      2
+
+#define AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN    0
+#define AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN  1
+#define AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_CLOSED  2
+#define AO_PAD_IGNITER_STATUS_UNKNOWN                  3
+
+void
+ao_pad_init(void);
+
+void
+ao_pad_disable(void);
+
+void
+ao_pad_enable(void);
+
+#endif /* _AO_PAD_H_ */
diff --git a/src/drivers/ao_pca9922.c b/src/drivers/ao_pca9922.c
new file mode 100644 (file)
index 0000000..6d8d18d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+/*
+ * PCA9922 LED driver. This uses SPI to send a single byte to the device to
+ * set the current state of the LEDs using the existing LED interface
+ */
+
+#include <ao.h>
+
+static __xdata uint8_t ao_led_state;
+
+static void
+ao_led_apply(void)
+{
+       /* Don't try the SPI bus during initialization */
+       if (!ao_cur_task)
+               return;
+       ao_spi_get_bit(AO_PCA9922_CS_PORT, AO_PCA9922_CS_PIN, AO_PCA9922_CS, AO_PCA9922_SPI_BUS, AO_SPI_SPEED_FAST);
+       ao_spi_send(&ao_led_state, 1, AO_PCA9922_SPI_BUS);
+       ao_spi_put_bit(AO_PCA9922_CS_PORT, AO_PCA9922_CS_PIN, AO_PCA9922_CS, AO_PCA9922_SPI_BUS);
+}
+
+void
+ao_led_on(uint8_t colors)
+{
+       ao_led_state |= colors;
+       ao_led_apply();
+}
+
+void
+ao_led_off(uint8_t colors)
+{
+       ao_led_state &= ~colors;
+       ao_led_apply();
+}
+
+void
+ao_led_set(uint8_t colors)
+{
+       ao_led_state = colors;
+       ao_led_apply();
+}
+
+void
+ao_led_set_mask(uint8_t colors, uint8_t mask)
+{
+       ao_led_state = (ao_led_state & ~mask) | (colors & mask);
+       ao_led_apply();
+}
+
+void
+ao_led_toggle(uint8_t colors)
+{
+       ao_led_state ^= colors;
+       ao_led_apply();
+}
+
+void
+ao_led_for(uint8_t colors, uint16_t ticks) __reentrant
+{
+       ao_led_on(colors);
+       ao_delay(ticks);
+       ao_led_off(colors);
+}
+
+void
+ao_led_init(uint8_t enable)
+{
+       (void) enable;
+       ao_enable_output(AO_PCA9922_CS_PORT, AO_PCA9922_CS_PIN, AO_PCA9922_CS, 1);
+}
diff --git a/src/drivers/ao_pyro_slave.c b/src/drivers/ao_pyro_slave.c
new file mode 100644 (file)
index 0000000..f07c2cb
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_product.h>
+#include <ao_companion.h>
+#include <ao_flight.h>
+#include <ao_pyro.h>
+
+struct ao_companion_command    ao_companion_command;
+
+static const struct ao_companion_setup ao_telepyro_setup = {
+       .board_id               = AO_idProduct_NUMBER,
+       .board_id_inverse       = ~AO_idProduct_NUMBER,
+       .update_period          = 50,
+       .channels               = AO_TELEPYRO_NUM_ADC,
+};
+
+struct ao_config ao_config;
+
+extern volatile __data uint16_t ao_tick_count;
+uint16_t ao_boost_tick;
+
+void ao_spi_slave(void)
+{
+       if (!ao_spi_slave_recv((uint8_t *) &ao_companion_command,
+                              sizeof (ao_companion_command)))
+               return;
+
+       /* Figure out the outbound data */
+       switch (ao_companion_command.command) {
+       case AO_COMPANION_SETUP:
+               ao_spi_slave_send((uint8_t *) &ao_telepyro_setup,
+                                 sizeof (ao_telepyro_setup));
+               break;
+       case AO_COMPANION_FETCH:
+               ao_spi_slave_send((uint8_t *) &ao_data_ring[ao_data_ring_prev(ao_data_head)].adc.adc,
+                                 AO_TELEPYRO_NUM_ADC * sizeof (uint16_t));
+               break;
+       case AO_COMPANION_NOTIFY:
+               /* Can't use ao_time because we have interrupts suspended */
+               if (ao_companion_command.flight_state < ao_flight_boost && ao_companion_command.flight_state >= ao_flight_boost)
+                       ao_boost_tick = ao_tick_count;
+               ao_wakeup(&ao_pyro_wakeup);
+               break;
+       default:
+               return;
+       }
+}
diff --git a/src/drivers/ao_quadrature.c b/src/drivers/ao_quadrature.c
new file mode 100644 (file)
index 0000000..6cc2467
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_quadrature.h>
+#include <ao_exti.h>
+#if AO_EVENT
+#include <ao_event.h>
+#define ao_quadrature_queue(q) ao_event_put_isr(AO_EVENT_QUADRATURE, q, ao_quadrature_count[q])
+#else
+#define ao_quadrature_queue(q)
+#endif
+
+__xdata int32_t ao_quadrature_count[AO_QUADRATURE_COUNT];
+
+static uint8_t ao_quadrature_state[AO_QUADRATURE_COUNT];
+
+#define BIT(a,b)       ((a) | ((b) << 1))
+#define STATE(old_a, old_b, new_a, new_b)      (((BIT(old_a, old_b) << 2) | BIT(new_a, new_b)))
+
+#define port(q)        AO_QUADRATURE_ ## q ## _PORT
+#define bita(q) AO_QUADRATURE_ ## q ## _A
+#define bitb(q) AO_QUADRATURE_ ## q ## _B
+
+#define ao_quadrature_update(q) do {                                   \
+               ao_quadrature_state[q] = ((ao_quadrature_state[q] & 3) << 2); \
+               ao_quadrature_state[q] |= ao_gpio_get(port(q), bita(q), 0); \
+               ao_quadrature_state[q] |= ao_gpio_get(port(q), bitb(q), 0) << 1; \
+       } while (0)
+       
+
+static void
+ao_quadrature_isr(void)
+{
+       uint8_t q;
+#if AO_QUADRATURE_COUNT > 0
+       ao_quadrature_update(0);
+#endif
+#if AO_QUADRATURE_COUNT > 1
+       ao_quadrature_update(1);
+#endif
+
+       for (q = 0; q < AO_QUADRATURE_COUNT; q++) {
+               switch (ao_quadrature_state[q]) {
+               case STATE(0, 1, 0, 0):
+                       ao_quadrature_count[q]++;
+                       break;
+               case STATE(1, 0, 0, 0):
+                       ao_quadrature_count[q]--;
+                       break;
+               default:
+                       continue;
+               }
+               ao_quadrature_queue(q);
+               ao_wakeup(&ao_quadrature_count[q]);
+       }
+}
+
+int32_t
+ao_quadrature_poll(uint8_t q)
+{
+       int32_t ret;
+       ao_arch_critical(ret = ao_quadrature_count[q];);
+       return ret;
+}
+
+int32_t
+ao_quadrature_wait(uint8_t q)
+{
+       ao_sleep(&ao_quadrature_count[q]);
+       return ao_quadrature_poll(q);
+}
+
+static void
+ao_quadrature_test(void)
+{
+       uint8_t q;
+
+       ao_cmd_decimal();
+       q = ao_cmd_lex_i;
+       for (;;) {
+               int32_t c;
+               flush();
+               c = ao_quadrature_wait(q);
+               printf ("new count %6d\n", c);
+               if (c == 100)
+                       break;
+       }
+}
+
+static const struct ao_cmds ao_quadrature_cmds[] = {
+       { ao_quadrature_test,   "q <unit>\0Test quadrature" },
+       { 0, NULL }
+};
+
+#define init(q) do {                                                   \
+               ao_enable_port(port(q));                                \
+                                                                       \
+               ao_exti_setup(port(q), bita(q),                         \
+                             AO_QUADRATURE_MODE|AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_MED, \
+                             ao_quadrature_isr);                       \
+               ao_exti_enable(port(q), bita(q));                       \
+                                                                       \
+               ao_exti_setup(port(q), bitb(q),                         \
+                             AO_QUADRATURE_MODE|AO_EXTI_MODE_FALLING|AO_EXTI_MODE_RISING|AO_EXTI_PRIORITY_MED, \
+                             ao_quadrature_isr);                       \
+               ao_exti_enable(port(q), bitb(q));                       \
+       } while (0)
+
+void
+ao_quadrature_init(void)
+{
+#if AO_QUADRATURE_COUNT > 0
+       init(0);
+#endif
+#if AO_QUADRATURE_COUNT > 1
+       init(1);
+#endif
+       ao_cmd_register(&ao_quadrature_cmds[0]);
+}
diff --git a/src/drivers/ao_quadrature.h b/src/drivers/ao_quadrature.h
new file mode 100644 (file)
index 0000000..d7dda68
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_QUADRATURE_H_
+#define _AO_QUADRATURE_H_
+
+extern __xdata int32_t ao_quadrature_count[AO_QUADRATURE_COUNT];
+
+int32_t
+ao_quadrature_wait(uint8_t q);
+
+int32_t
+ao_quadrature_poll(uint8_t q);
+
+void
+ao_quadrature_init(void);
+
+#endif /* _AO_QUADRATURE_H_ */
diff --git a/src/drivers/ao_radio_master.c b/src/drivers/ao_radio_master.c
new file mode 100644 (file)
index 0000000..73ac3c0
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_radio_spi.h>
+#include <ao_exti.h>
+#include <ao_radio_cmac.h>
+
+static __xdata struct ao_radio_spi_reply       ao_radio_spi_reply;
+static __xdata struct ao_radio_spi_request     ao_radio_spi_request;
+static volatile __xdata uint8_t                        ao_radio_wait_mode;
+static volatile __xdata uint8_t                        ao_radio_done = 0;
+static volatile __xdata uint8_t                        ao_radio_ready = 1;
+static __xdata uint8_t                         ao_radio_mutex;
+static __xdata uint8_t                         ao_radio_aes_seq;
+
+__xdata int8_t                                 ao_radio_cmac_rssi;
+
+#if 0
+#define PRINTD(...) do { printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
+#else
+#define PRINTD(...) 
+#endif
+
+static void
+ao_radio_isr(void)
+{
+       if (ao_gpio_get(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN, AO_RADIO_INT)) {
+               ao_radio_ready = 1;
+               ao_wakeup((void *) &ao_radio_ready);
+       } else {
+               ao_radio_done = 1;
+               ao_wakeup((void *) &ao_radio_done);
+       }
+}
+
+static void
+ao_radio_master_start(void)
+{
+       ao_spi_get_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
+                      AO_RADIO_SPI_BUS,
+                      AO_SPI_SPEED_200kHz);
+}
+
+static void
+ao_radio_master_stop(void)
+{
+       ao_spi_put_bit(AO_RADIO_CS_PORT, AO_RADIO_CS_PIN, AO_RADIO_CS,
+                      AO_RADIO_SPI_BUS);
+}
+
+static uint8_t
+ao_radio_master_send(void)
+{
+       uint8_t ret;
+
+       PRINTD("send %d\n", ao_radio_spi_request.len);
+       ao_radio_done = 0;
+
+       /* Wait for radio chip to be ready for a command
+        */
+
+       PRINTD("Waiting radio ready\n");
+       cli();
+       ao_radio_ready = ao_gpio_get(AO_RADIO_INT_PORT,
+                                    AO_RADIO_INT_PIN, AO_RADIO_INT);
+       ret = 0;
+       while (!ao_radio_ready) {
+               ret = ao_sleep((void *) &ao_radio_ready);
+               if (ret)
+                       break;
+       }
+       sei();
+       if (ret)
+               return 0;
+
+       PRINTD("radio_ready %d radio_done %d\n", ao_radio_ready, ao_radio_done);
+
+       /* Send the command
+        */
+       ao_radio_wait_mode = 0;
+       ao_radio_master_start();
+       ao_spi_send(&ao_radio_spi_request,
+                   ao_radio_spi_request.len,
+                   AO_RADIO_SPI_BUS);
+       ao_radio_master_stop();
+       PRINTD("waiting for send done %d\n", ao_radio_done);
+       cli();
+       while (!ao_radio_done)
+               if (ao_sleep((void *) &ao_radio_done))
+                       break;
+       sei();
+       PRINTD ("sent, radio done %d isr_0 %d isr_1 %d\n", ao_radio_done, isr_0_count, isr_1_count);
+       return ao_radio_done;
+}
+
+static void
+ao_radio_get(uint8_t req, uint8_t len)
+{
+       ao_config_get();
+       ao_mutex_get(&ao_radio_mutex);
+       ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN + len;
+       ao_radio_spi_request.request = req;
+       ao_radio_spi_request.setting = ao_config.radio_setting;
+}
+
+static void
+ao_radio_put(void)
+{
+       ao_mutex_put(&ao_radio_mutex);
+}
+
+static void
+ao_radio_get_data(__xdata void *d, uint8_t size)
+{
+       PRINTD ("fetch\n");
+       ao_radio_master_start();
+       ao_spi_recv(&ao_radio_spi_reply,
+                   AO_RADIO_SPI_REPLY_HEADER_LEN + size,
+                   AO_RADIO_SPI_BUS);
+       ao_radio_master_stop();
+       ao_xmemcpy(d, ao_radio_spi_reply.payload, size);
+       PRINTD ("fetched %d\n", size);
+}
+
+void
+ao_radio_recv_abort(void)
+{
+       ao_radio_get(AO_RADIO_SPI_RECV_ABORT, 0);
+       ao_radio_master_send();
+       ao_radio_put();
+}
+
+void
+ao_radio_send(const void *d, uint8_t size)
+{
+       ao_radio_get(AO_RADIO_SPI_SEND, size);
+       ao_xmemcpy(&ao_radio_spi_request.payload, d, size);
+       ao_radio_master_send();
+       ao_radio_put();
+}
+
+
+uint8_t
+ao_radio_recv(__xdata void *d, uint8_t size)
+{
+       int8_t  ret;
+       uint8_t recv;
+
+       /* Recv the data
+        */
+       
+       ao_radio_get(AO_RADIO_SPI_RECV, 0);
+       ao_radio_spi_request.recv_len = size;
+       recv = ao_radio_master_send();
+       if (!recv) {
+               ao_radio_put();
+               ao_radio_recv_abort();
+               return 0;
+       }
+       ao_radio_get_data(d, size);
+       recv = ao_radio_spi_reply.status;
+       ao_radio_put();
+
+       return recv;
+}
+
+static void
+ao_radio_cmac_set_key(void)
+{
+       if (ao_radio_aes_seq == ao_config_aes_seq)
+               return;
+       /* Set the key.
+        */
+       PRINTD ("set key\n");
+       ao_radio_get(AO_RADIO_SPI_CMAC_KEY, AO_AES_LEN);
+       ao_xmemcpy(&ao_radio_spi_request.payload, &ao_config.aes_key, AO_AES_LEN);
+       ao_radio_master_send();
+       ao_radio_put();
+       PRINTD ("key set\n");
+       ao_radio_aes_seq = ao_config_aes_seq;
+}
+
+int8_t
+ao_radio_cmac_send(__xdata void *packet, uint8_t len) __reentrant
+{
+       if (len > AO_CMAC_MAX_LEN)
+               return AO_RADIO_CMAC_LEN_ERROR;
+
+       ao_radio_cmac_set_key();
+
+       PRINTD ("cmac_send: send %d\n", len);
+
+       /* Send the data
+        */
+       
+       PRINTD ("sending packet\n");
+       ao_radio_get(AO_RADIO_SPI_CMAC_SEND, len);
+       ao_xmemcpy(&ao_radio_spi_request.payload, packet, len);
+       ao_radio_master_send();
+       ao_radio_put();
+       PRINTD ("packet sent\n");
+       return AO_RADIO_CMAC_OK;
+}
+
+int8_t
+ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentrant
+{
+       int8_t  ret;
+       int8_t  recv;
+
+       if (len > AO_CMAC_MAX_LEN)
+               return AO_RADIO_CMAC_LEN_ERROR;
+
+       ao_radio_cmac_set_key();
+
+       /* Recv the data
+        */
+       PRINTD ("queuing recv\n");
+       ao_radio_get(AO_RADIO_SPI_CMAC_RECV, 0);
+       ao_radio_spi_request.recv_len = len;
+       ao_radio_spi_request.timeout = timeout;
+       recv = ao_radio_master_send();
+       PRINTD ("recv queued: %d\n", recv);
+       if (!recv) {
+               ao_radio_put();
+               ao_radio_recv_abort();
+               return AO_RADIO_CMAC_TIMEOUT;
+       }
+
+       PRINTD ("fetching data\n");
+       ao_radio_get_data(packet, len);
+       recv = ao_radio_spi_reply.status;
+       ao_radio_cmac_rssi = ao_radio_spi_reply.rssi;
+       ao_radio_put();
+       PRINTD ("data fetched: %d %d\n", recv, ao_radio_cmac_rssi);
+       return recv;
+}
+
+static uint8_t ao_radio_test_on;
+
+void
+ao_radio_test(uint8_t on)
+{
+       if (on) {
+               if (!ao_radio_test_on) {
+                       ao_radio_get(AO_RADIO_SPI_TEST_ON, 0);
+                       ao_radio_test_on = 1;
+                       ao_radio_master_send();
+               }
+       } else {
+               if (ao_radio_test_on) {
+                       ao_radio_spi_request.len = AO_RADIO_SPI_REQUEST_HEADER_LEN;
+                       ao_radio_spi_request.request = AO_RADIO_SPI_TEST_OFF;
+                       ao_radio_master_send();
+                       ao_radio_test_on = 0;
+                       ao_radio_put();
+               }
+       }
+}
+
+static void
+ao_radio_test_cmd(void)
+{
+       uint8_t mode = 2;
+       ao_cmd_white();
+       if (ao_cmd_lex_c != '\n') {
+               ao_cmd_decimal();
+               mode = (uint8_t) ao_cmd_lex_u32;
+       }
+       mode++;
+       if ((mode & 2))
+               ao_radio_test(1);
+       if (mode == 3) {
+               printf ("Hit a character to stop..."); flush();
+               getchar();
+               putchar('\n');
+       }
+       if ((mode & 1))
+               ao_radio_test(0);
+}
+
+__code struct ao_cmds ao_radio_cmds[] = {
+       { ao_radio_test_cmd,    "C <1 start, 0 stop, none both>\0Radio carrier test" },
+       { 0,    NULL },
+};
+
+void
+ao_radio_init(void)
+{
+       ao_spi_init_cs(AO_RADIO_CS_PORT, (1 << AO_RADIO_CS_PIN));
+
+       ao_enable_port(AO_RADIO_INT_PORT);
+       ao_exti_setup(AO_RADIO_INT_PORT,
+                     AO_RADIO_INT_PIN,
+                     AO_EXTI_MODE_RISING|AO_EXTI_MODE_FALLING,
+                     ao_radio_isr);
+       ao_exti_enable(AO_RADIO_INT_PORT, AO_RADIO_INT_PIN);
+       ao_cmd_register(&ao_radio_cmds[0]);
+}
diff --git a/src/drivers/ao_radio_slave.c b/src/drivers/ao_radio_slave.c
new file mode 100644 (file)
index 0000000..1d1f16f
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_radio_spi.h>
+#include <ao_radio_cmac.h>
+
+static __xdata struct ao_radio_spi_reply ao_radio_spi_reply;
+
+static __xdata struct ao_radio_spi_request ao_radio_spi_request;
+
+static __xdata uint8_t slave_state;
+
+static void
+ao_radio_slave_low(void)
+{
+       uint16_t        i;
+
+       if (slave_state != 1)
+               ao_panic(1);
+       ao_gpio_set(AO_RADIO_SLAVE_INT_PORT, AO_RADIO_SLAVE_INT_BIT, AO_RADIO_SLAVE_INT_PIN, 0);
+       for (i = 0; i < 1000; i++)
+               ao_arch_nop();
+       slave_state = 0;
+}
+
+static void
+ao_radio_slave_high(void)
+{
+       if (slave_state != 0)
+               ao_panic(2);
+       ao_gpio_set(AO_RADIO_SLAVE_INT_PORT, AO_RADIO_SLAVE_INT_BIT, AO_RADIO_SLAVE_INT_PIN, 1);
+       slave_state = 1;
+}
+
+static void
+ao_radio_slave_spi(void)
+{
+       ao_spi_get_slave(AO_RADIO_SLAVE_BUS);
+       for (;;) {
+               ao_spi_recv(&ao_radio_spi_request,
+                           (2 << 13) | sizeof (ao_radio_spi_request),
+                           AO_RADIO_SLAVE_BUS);
+               ao_radio_slave_high();
+               ao_spi_recv_wait();
+               switch (ao_radio_spi_request.request) {
+               case AO_RADIO_SPI_RECV:
+
+                       /* XXX monitor CS to interrupt the receive */
+
+                       ao_config.radio_setting = ao_radio_spi_request.setting;
+                       ao_led_on(AO_LED_RX);
+                       ao_radio_spi_reply.status = ao_radio_recv(&ao_radio_spi_reply.payload,
+                                                                 ao_radio_spi_request.recv_len);
+                       ao_led_off(AO_LED_RX);
+                       ao_radio_spi_reply.rssi = 0;
+                       ao_spi_send(&ao_radio_spi_reply,
+                                   AO_RADIO_SPI_REPLY_HEADER_LEN + ao_radio_spi_request.recv_len,
+                                   AO_RADIO_SLAVE_BUS);
+                       ao_radio_slave_low();
+                       ao_spi_send_wait();
+                       continue;
+               case AO_RADIO_SPI_CMAC_RECV:
+                       ao_config.radio_setting = ao_radio_spi_request.setting;
+                       ao_led_on(AO_LED_RX);
+                       ao_radio_spi_reply.status = ao_radio_cmac_recv(&ao_radio_spi_reply.payload,
+                                                                      ao_radio_spi_request.recv_len,
+                                                                      ao_radio_spi_request.timeout);
+                       ao_led_off(AO_LED_RX);
+                       ao_radio_spi_reply.rssi = ao_radio_cmac_rssi;
+                       ao_spi_send(&ao_radio_spi_reply,
+                                   AO_RADIO_SPI_REPLY_HEADER_LEN + ao_radio_spi_request.recv_len,
+                                   AO_RADIO_SLAVE_BUS);
+                       ao_radio_slave_low();
+                       ao_spi_send_wait();
+                       continue;
+               case AO_RADIO_SPI_SEND:
+                       ao_config.radio_setting = ao_radio_spi_request.setting;
+                       ao_led_on(AO_LED_TX);
+                       ao_radio_send(&ao_radio_spi_request.payload,
+                                     ao_radio_spi_request.len - AO_RADIO_SPI_REQUEST_HEADER_LEN);
+                       ao_led_off(AO_LED_TX);
+                       break;
+
+               case AO_RADIO_SPI_CMAC_SEND:
+                       ao_config.radio_setting = ao_radio_spi_request.setting;
+                       ao_led_on(AO_LED_TX);
+                       ao_radio_cmac_send(&ao_radio_spi_request.payload,
+                                          ao_radio_spi_request.len - AO_RADIO_SPI_REQUEST_HEADER_LEN);
+                       ao_led_off(AO_LED_TX);
+                       break;
+                       
+               case AO_RADIO_SPI_CMAC_KEY:
+                       ao_xmemcpy(&ao_config.aes_key, ao_radio_spi_request.payload, AO_AES_LEN);
+                       break;
+
+               case AO_RADIO_SPI_TEST_ON:
+                       ao_config.radio_setting = ao_radio_spi_request.setting;
+                       ao_radio_test(1);
+                       break;
+
+               case AO_RADIO_SPI_TEST_OFF:
+                       ao_radio_test(0);
+                       break;
+               }
+               ao_radio_slave_low();
+       }
+}
+
+static __xdata struct ao_task ao_radio_slave_spi_task;
+
+void
+ao_radio_slave_init(void)
+{
+       ao_add_task(&ao_radio_slave_spi_task, ao_radio_slave_spi, "radio_spi");
+       ao_enable_output(AO_RADIO_SLAVE_INT_PORT, AO_RADIO_SLAVE_INT_BIT, AO_RADIO_SLAVE_INT_PIN, 0);
+}
diff --git a/src/drivers/ao_radio_spi.h b/src/drivers/ao_radio_spi.h
new file mode 100644 (file)
index 0000000..2957f70
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_RADIO_SPI_H_
+#define _AO_RADIO_SPI_H_
+
+#define AO_RADIO_SPI_RECV      0
+#define AO_RADIO_SPI_RECV_ABORT        1
+#define AO_RADIO_SPI_RECV_FETCH        2
+#define AO_RADIO_SPI_SEND      3
+
+#define AO_RADIO_SPI_CMAC_KEY  4
+#define AO_RADIO_SPI_CMAC_RECV 5
+#define AO_RADIO_SPI_CMAC_SEND 6
+
+#define AO_RADIO_SPI_TEST_ON   7
+#define AO_RADIO_SPI_TEST_OFF  8
+
+#define AO_RADIO_SPI_MAX_PAYLOAD       128
+
+struct ao_radio_spi_request {
+       uint8_t         len;            /* required to be first by cc1111 DMA engine */
+       uint8_t         request;
+       uint8_t         recv_len;
+       uint8_t         pad;
+       uint32_t        setting;
+       uint16_t        timeout;
+       uint8_t         payload[AO_RADIO_SPI_MAX_PAYLOAD];
+};
+
+#define AO_RADIO_SPI_REQUEST_HEADER_LEN        (sizeof (struct ao_radio_spi_request) - AO_RADIO_SPI_MAX_PAYLOAD)
+
+struct ao_radio_spi_reply {
+       uint8_t         status;
+       int8_t          rssi;
+       uint8_t         payload[AO_RADIO_SPI_MAX_PAYLOAD];
+};
+
+#define AO_RADIO_SPI_REPLY_HEADER_LEN  (sizeof (struct ao_radio_spi_reply) - AO_RADIO_SPI_MAX_PAYLOAD)
+
+void
+ao_radio_slave_init(void);
+
+#endif /* _AO_RADIO_SPI_H_ */
diff --git a/src/drivers/ao_science_slave.c b/src/drivers/ao_science_slave.c
new file mode 100644 (file)
index 0000000..996e98d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_product.h"
+#include "ao_flight.h"
+#include "ao_companion.h"
+
+struct ao_companion_command    ao_companion_command;
+
+static const struct ao_companion_setup ao_telescience_setup = {
+       .board_id               = AO_idProduct_NUMBER,
+       .board_id_inverse       = ~AO_idProduct_NUMBER,
+       .update_period          = 50,
+       .channels               = AO_LOG_TELESCIENCE_NUM_ADC,
+};
+
+void ao_spi_slave(void)
+{
+       if (!ao_spi_slave_recv((uint8_t *) &ao_companion_command,
+                              sizeof (ao_companion_command)))
+               return;
+
+       /* Figure out the outbound data */
+       switch (ao_companion_command.command) {
+       case AO_COMPANION_SETUP:
+               ao_spi_slave_send((uint8_t *) &ao_telescience_setup,
+                                 sizeof (ao_telescience_setup));
+               break;
+       case AO_COMPANION_FETCH:
+               ao_spi_slave_send((uint8_t *) &ao_data_ring[ao_data_ring_prev(ao_data_head)].adc,
+                                 AO_LOG_TELESCIENCE_NUM_ADC * sizeof (uint16_t));
+               break;
+       case AO_COMPANION_NOTIFY:
+               break;
+       default:
+               return;
+       }
+
+#if HAS_LOG
+       ao_log_single_write_data.telescience.tm_tick = ao_companion_command.tick;
+       if (ao_log_single_write_data.telescience.tm_state != ao_companion_command.flight_state) {
+               ao_log_single_write_data.telescience.tm_state = ao_companion_command.flight_state;
+               if (ao_flight_boost <= ao_log_single_write_data.telescience.tm_state) {
+                       if (ao_log_single_write_data.telescience.tm_state < ao_flight_landed)
+                               ao_log_single_start();
+                       else
+                               ao_log_single_stop();
+               }
+       }
+#endif
+}
diff --git a/src/drivers/ao_seven_segment.c b/src/drivers/ao_seven_segment.c
new file mode 100644 (file)
index 0000000..b3b5f87
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_seven_segment.h>
+#include <ao_lcd_stm.h>
+
+/*
+ *         0
+ *     -------
+ *     |       |  
+ *   1 |       | 2
+ *     |   3   |
+ *      -------
+ *     |       |
+ *   4 |       | 5
+ *     |   6   |
+ *      -------
+ *              [] 7
+ *
+ */
+
+static const uint8_t ao_segments[] = {
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (0 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* 0 */
+
+       (0 << AO_SEGMENT_0) |
+       (0 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (0 << AO_SEGMENT_3) |
+       (0 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (0 << AO_SEGMENT_6),            /* 1 */
+
+       (1 << AO_SEGMENT_0) |
+       (0 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (0 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* 2 */
+
+       (1 << AO_SEGMENT_0) |
+       (0 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (0 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* 3 */
+
+       (0 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (0 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (0 << AO_SEGMENT_6),            /* 4 */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (0 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (0 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* 5 */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (0 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* 6 */
+
+       (1 << AO_SEGMENT_0) |
+       (0 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (0 << AO_SEGMENT_3) |
+       (0 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (0 << AO_SEGMENT_6),            /* 7 */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* 8 */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (0 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* 9 */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (0 << AO_SEGMENT_6),            /* A */
+
+       (0 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (0 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* b */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (0 << AO_SEGMENT_2) |
+       (0 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (0 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* c */
+
+       (0 << AO_SEGMENT_0) |
+       (0 << AO_SEGMENT_1) |
+       (1 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (1 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* d */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (0 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (0 << AO_SEGMENT_5) |
+       (1 << AO_SEGMENT_6),            /* E */
+
+       (1 << AO_SEGMENT_0) |
+       (1 << AO_SEGMENT_1) |
+       (0 << AO_SEGMENT_2) |
+       (1 << AO_SEGMENT_3) |
+       (1 << AO_SEGMENT_4) |
+       (0 << AO_SEGMENT_5) |
+       (0 << AO_SEGMENT_6),            /* F */
+};
+
+void
+ao_seven_segment_set(uint8_t digit, uint8_t value)
+{
+       uint8_t s;
+       uint8_t segments;
+
+       if (value == AO_SEVEN_SEGMENT_CLEAR)
+               segments = 0;
+       else {
+               segments = ao_segments[value & 0xf];
+
+               /* Check for decimal point */
+               if (value & 0x10)
+                       segments |= (1 << AO_SEGMENT_7);
+       }
+
+       for (s = 0; s <= 7; s++)
+               ao_lcd_set(digit, s, !!(segments & (1 << s)));
+       ao_lcd_flush();
+}
+
+void
+ao_seven_segment_clear(void)
+{
+       ao_lcd_clear();
+}
+
+
+#if 0
+static void
+ao_seven_segment_show(void)
+{
+       uint8_t digit, value;
+       ao_cmd_decimal();
+       digit = ao_cmd_lex_i;
+       ao_cmd_decimal();
+       value = ao_cmd_lex_i;
+       ao_seven_segment_set(digit, value);
+}
+
+
+static const struct ao_cmds ao_seven_segment_cmds[] = {
+       { ao_seven_segment_show,        "S <digit> <value>\0Set LCD digit" },
+       { 0, NULL },
+};
+#endif
+
+void
+ao_seven_segment_init(void)
+{
+#if 0
+       ao_cmd_register(ao_seven_segment_cmds);
+#endif
+}
diff --git a/src/drivers/ao_seven_segment.h b/src/drivers/ao_seven_segment.h
new file mode 100644 (file)
index 0000000..5b29dea
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_SEVEN_SEGMENT_H_
+#define _AO_SEVEN_SEGMENT_H_
+
+#define AO_SEVEN_SEGMENT_DECIMAL       0x10
+
+#define AO_SEVEN_SEGMENT_CLEAR         0xff
+
+void
+ao_seven_segment_set(uint8_t digit, uint8_t value);
+
+void
+ao_seven_segment_clear(void);
+
+void
+ao_seven_segment_init(void);
+
+#endif /* _AO_SEVEN_SEGMENT_H_ */
diff --git a/src/gps-cksum b/src/gps-cksum
deleted file mode 100755 (executable)
index a08153b..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env nickle
-
-int checksum(string a)
-{
-       int     c = 0;
-       for (int i = 0; i < String::length(a); i++)
-               c ^= a[i];
-       return c;
-}
-
-void main()
-{
-       for (int i = 1; i < dim(argv); i++)
-               printf ("$%s*%02x\n", argv[i], checksum(argv[i]));
-}
-
-main();
diff --git a/src/kalman/kalman.5c b/src/kalman/kalman.5c
new file mode 100755 (executable)
index 0000000..cfb7abe
--- /dev/null
@@ -0,0 +1,491 @@
+#!/usr/bin/env nickle
+
+/*
+ * Copyright © 2011 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.
+ */
+
+autoimport ParseArgs;
+
+load "load_csv.5c"
+import load_csv;
+
+load "matrix.5c"
+import matrix;
+
+load "kalman_filter.5c"
+import kalman;
+
+/*
+ * AltOS keeps speed and accel scaled
+ * by 4 bits to provide additional precision
+ */
+real   height_scale = 1.0;
+real   accel_scale = 16.0;
+real   speed_scale = 16.0;
+
+/*
+ * State:
+ *
+ * x[0] = height
+ * x[1] = velocity
+ * x[2] = acceleration
+ */
+
+/*
+ * Measurement
+ *
+ * z[0] = height
+ * z[1] = acceleration
+ */
+
+real default_σ_m = 5;
+real default_σ_h = 20;
+real default_σ_a = 2;
+
+parameters_t param_both(real t, real σ_m, real σ_h, real σ_a) {
+       if (σ_m == 0)
+               σ_m = default_σ_m;
+       if (σ_h == 0)
+               σ_h = default_σ_h;
+       if (σ_a == 0)
+               σ_a = default_σ_a;
+
+       σ_m = imprecise(σ_m) * accel_scale;
+       σ_h = imprecise(σ_h) * height_scale;
+       σ_a = imprecise(σ_a) * accel_scale;
+
+       t = imprecise(t);
+
+       return (parameters_t) {
+/*
+ * Equation computing state k from state k-1
+ *
+ * height = height- + velocity- * t + acceleration- * t² / 2
+ * velocity = velocity- + acceleration- * t
+ * acceleration = acceleration-
+ */
+               .a = (real[3,3]) {
+                       { 1,
+                         t * height_scale / speed_scale , t**2/2 * height_scale / accel_scale },
+                       { 0, 1, t * speed_scale / accel_scale },
+                       { 0, 0, 1 }
+               },
+/*
+ * Model error covariance. The only inaccuracy in the
+ * model is the assumption that acceleration is constant
+ */
+               .q = (real[3,3]) {
+                       { 0, 0, 0 },
+                       { 0, 0, 0 },
+                       {.0, 0, σ_m**2 },
+               },
+/*
+ * Measurement error covariance
+ * Our sensors are independent, so
+ * this matrix is zero off-diagonal
+ */
+               .r = (real[2,2]) {
+                       { σ_h ** 2, 0 },
+                       { 0, σ_a ** 2 },
+               },
+/*
+ * Extract measurements from state,
+ * this just pulls out the height and acceleration
+ * values.
+ */
+               .h = (real[2,3]) {
+                       { 1, 0, 0 },
+                       { 0, 0, 1 },
+               },
+        };
+}
+
+parameters_t param_baro(real t, real σ_m, real σ_h) {
+       if (σ_m == 0)
+               σ_m = default_σ_m;
+       if (σ_h == 0)
+               σ_h = default_σ_h;
+
+       σ_m = imprecise(σ_m) * accel_scale;
+       σ_h = imprecise(σ_h) * height_scale;
+
+       t = imprecise(t);
+       return (parameters_t) {
+/*
+ * Equation computing state k from state k-1
+ *
+ * height = height- + velocity- * t + acceleration- * t² / 2
+ * velocity = velocity- + acceleration- * t
+ * acceleration = acceleration-
+ */
+               .a = (real[3,3]) {
+                       { 1, t * height_scale / speed_scale , t**2/2 * height_scale / accel_scale },
+                       { 0, 1, t * speed_scale / accel_scale },
+                       { 0, 0, 1 }
+               },
+/*
+ * Model error covariance. The only inaccuracy in the
+ * model is the assumption that acceleration is constant
+ */
+               .q = (real[3,3]) {
+                       { 0, 0, 0 },
+                       { 0, 0, 0 },
+                       {.0, 0, σ_m**2 },
+               },
+/*
+ * Measurement error covariance
+ * Our sensors are independent, so
+ * this matrix is zero off-diagonal
+ */
+               .r = (real[1,1]) {
+                       { σ_h ** 2 },
+               },
+/*
+ * Extract measurements from state,
+ * this just pulls out the height
+ * values.
+ */
+               .h = (real[1,3]) {
+                       { 1, 0, 0 },
+               },
+        };
+}
+
+parameters_t param_accel(real t, real σ_m, real σ_a) {
+       if (σ_m == 0)
+               σ_m = default_σ_m;
+       if (σ_a == 0)
+               σ_a = default_σ_a;
+
+       σ_m = imprecise(σ_m) * accel_scale;
+       σ_a = imprecise(σ_a) * accel_scale;
+
+       t = imprecise(t);
+       return (parameters_t) {
+/*
+ * Equation computing state k from state k-1
+ *
+ * height = height- + velocity- * t + acceleration- * t² / 2
+ * velocity = velocity- + acceleration- * t
+ * acceleration = acceleration-
+ */
+               .a = (real[3,3]) {
+                       { 1, t * height_scale / speed_scale , t**2/2 * height_scale / accel_scale },
+                       { 0, 1, t * speed_scale / accel_scale },
+                       { 0, 0, 1 }
+               },
+/*
+ * Model error covariance. The only inaccuracy in the
+ * model is the assumption that acceleration is constant
+ */
+               .q = (real[3,3]) {
+                       { 0, 0, 0 },
+                       { 0, 0, 0 },
+                       {.0, 0, σ_m**2 },
+               },
+/*
+ * Measurement error covariance
+ * Our sensors are independent, so
+ * this matrix is zero off-diagonal
+ */
+               .r = (real[1,1]) {
+                       { σ_a ** 2 },
+               },
+/*
+ * Extract measurements from state,
+ * this just pulls out the acceleration
+ * values.
+ */
+               .h = (real[1,3]) {
+                       { 0, 0, 1 },
+               },
+        };
+}
+
+parameters_t param_vel(real t) {
+       static real σ_m = .1;
+       static real σ_v = imprecise(10);
+
+       return (parameters_t) {
+/*
+ * Equation computing state k from state k-1
+ *
+ * height = height- + velocity- * t + acceleration- * t² / 2
+ * velocity = velocity- + acceleration- * t
+ * acceleration = acceleration-
+ */
+               .a = (real[3,3]) {
+                       { 1, imprecise(t), imprecise((t**2)/2) },
+                       { 0, 1, imprecise(t) },
+                       { 0, 0, 1 }
+               },
+/*
+ * Model error covariance. The only inaccuracy in the
+ * model is the assumption that acceleration is constant
+ */
+               .q = (real[3,3]) {
+                       { 0, 0, 0 },
+                       { 0, 0, 0 },
+                       {.0, 0, σ_m**2 },
+               },
+/*
+ * Measurement error covariance
+ * Our sensors are independent, so
+ * this matrix is zero off-diagonal
+ */
+               .r = (real[1,1]) {
+                       { σ_v ** 2 },
+               },
+/*
+ * Extract measurements from state,
+ * this just pulls out the velocity
+ * values.
+ */
+               .h = (real[1,3]) {
+                       { 0, 1, 0 },
+               },
+        };
+}
+
+real   max_baro_height = 18000;
+
+bool   just_kalman = true;
+real   accel_input_scale = 1;
+
+void run_flight(string name, file f, bool summary) {
+       state_t current_both = {
+               .x = (real[3]) { 0, 0, 0 },
+               .p = (real[3,3]) { { 0 ... } ... },
+       };
+       state_t current_accel = current_both;
+       state_t current_baro = current_both;
+       real    t;
+       real    kalman_apogee_time = -1;
+       real    kalman_apogee = 0;
+       real    raw_apogee_time_first;
+       real    raw_apogee_time_last;
+       real    raw_apogee = 0;
+       real    default_descent_rate = 20;
+       real    speed = 0;
+       real    prev_acceleration = 0;
+       state_t apogee_state;
+       parameters_fast_t       fast_both;
+       parameters_fast_t       fast_baro;
+       parameters_fast_t       fast_accel;
+       real                    fast_delta_t = 0;
+       bool                    fast = true;
+
+       for (;;) {
+               record_t        record = parse_record(f, accel_input_scale);
+               if (record.done)
+                       break;
+               if (is_uninit(&t))
+                       t = record.time;
+               real delta_t = record.time - t;
+               if (delta_t <= 0)
+                       continue;
+               t = record.time;
+               if (record.height > raw_apogee) {
+                       raw_apogee_time_first = record.time;
+                       raw_apogee = record.height;
+               }
+               if (record.height == raw_apogee)
+                       raw_apogee_time_last = record.time;
+
+               real    acceleration = record.acceleration;
+               real    height = record.height;
+
+               speed = (speed + (acceleration + prev_acceleration / 2) * delta_t);
+               prev_acceleration = acceleration;
+
+               vec_t z_both = (real[2]) { record.height * height_scale,  record.acceleration * accel_scale };
+               vec_t z_accel = (real[1]) { record.acceleration * accel_scale };
+               vec_t z_baro = (real[1]) { record.height * height_scale };
+
+
+               if (fast) {
+                       if (delta_t != fast_delta_t) {
+                               fast_both = convert_to_fast(param_both(delta_t, 0, 0, 0));
+                               fast_accel = convert_to_fast(param_accel(delta_t, 0, 0));
+                               fast_baro = convert_to_fast(param_baro(delta_t, 0, 0));
+                               fast_delta_t = delta_t;
+                       }
+
+                       current_both.x = predict_fast(current_both.x, fast_both);
+                       current_accel.x = predict_fast(current_accel.x, fast_accel);
+                       current_baro.x = predict_fast(current_baro.x, fast_baro);
+
+                       current_both.x = correct_fast(current_both.x, z_both, fast_both);
+                       current_accel.x = correct_fast(current_accel.x, z_accel, fast_accel);
+                       current_baro.x = correct_fast(current_baro.x, z_baro, fast_baro);
+               } else {
+                       parameters_t p_both = param_both(delta_t, 0, 0, 0);
+                       parameters_t p_accel = param_accel(delta_t, 0, 0);
+                       parameters_t p_baro = param_baro(delta_t, 0, 0);
+
+                       state_t pred_both = predict(current_both, p_both);
+                       state_t pred_accel = predict(current_accel, p_accel);
+                       state_t pred_baro = predict(current_baro, p_baro);
+
+                       state_t next_both = correct(pred_both, z_both, p_both);
+                       state_t next_accel = correct(pred_accel, z_accel, p_accel);
+                       state_t next_baro = correct(pred_baro, z_baro, p_baro);
+                       current_both = next_both;
+                       current_accel = next_accel;
+                       current_baro = next_baro;
+               }
+
+               printf ("%16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f %16.8f\n",
+                       record.time,
+                       record.height, speed, record.acceleration,
+                       current_both.x[0] / height_scale, current_both.x[1] / speed_scale, current_both.x[2] / accel_scale,
+                       current_accel.x[0] / height_scale, current_accel.x[1] / speed_scale, current_accel.x[2] / accel_scale,
+                       current_baro.x[0] / height_scale, current_baro.x[1] / speed_scale, current_baro.x[2] / accel_scale);
+               if (kalman_apogee_time < 0) {
+                       if (current_both.x[1] < -1 && current_accel.x[1] < -1 && current_baro.x[1] < -1) {
+                               kalman_apogee = current_both.x[0];
+                               kalman_apogee_time = record.time;
+                               break;
+                       }
+               }
+       }
+       real raw_apogee_time = (raw_apogee_time_last + raw_apogee_time_first) / 2;
+       if (summary && !just_kalman) {
+               printf("%s: kalman (%8.2f m %6.2f s) raw (%8.2f m %6.2f s) error %6.2f s\n",
+                      name,
+                      kalman_apogee, kalman_apogee_time,
+                      raw_apogee, raw_apogee_time,
+                      kalman_apogee_time - raw_apogee_time);
+       }
+}
+
+void main() {
+       bool    summary = false;
+       int     user_argind = 1;
+       real    time_step = 0.01;
+       string  compute = "none";
+       string  prefix = "AO_K";
+       real    σ_m = 1;
+       real    σ_h = 4;
+       real    σ_a = 1;
+
+       ParseArgs::argdesc argd = {
+               .args = {
+                       { .var = { .arg_flag = &summary },
+                         .abbr = 's',
+                         .name = "summary",
+                         .desc = "Print a summary of the flight" },
+                       { .var = { .arg_real = &max_baro_height },
+                         .abbr = 'm',
+                         .name = "maxbaro",
+                         .expr_name = "height",
+                         .desc = "Set maximum usable barometer height" },
+                       { .var = { .arg_real = &accel_input_scale, },
+                         .abbr = 'a',
+                         .name = "accel",
+                         .expr_name = "<accel-scale>",
+                         .desc = "Set accelerometer scale factor" },
+                       { .var = { .arg_real = &time_step, },
+                         .abbr = 't',
+                         .name = "time",
+                         .expr_name = "<time-step>",
+                         .desc = "Set time step for convergence" },
+                       { .var = { .arg_string = &prefix },
+                         .abbr = 'p',
+                         .name = "prefix",
+                         .expr_name = "<prefix>",
+                         .desc = "Prefix for compute output" },
+                       { .var = { .arg_string = &compute },
+                         .abbr = 'c',
+                         .name = "compute",
+                         .expr_name = "{both,baro,accel}",
+                         .desc = "Compute Kalman factor through convergence" },
+                       { .var = { .arg_real = &σ_m },
+                         .abbr = 'M',
+                         .name = "model",
+                         .expr_name = "<model-accel-error>",
+                         .desc = "Model co-variance for acceleration" },
+                       { .var = { .arg_real = &σ_h },
+                         .abbr = 'H',
+                         .name = "height",
+                         .expr_name = "<measure-height-error>",
+                         .desc = "Measure co-variance for height" },
+                       { .var = { .arg_real = &σ_a },
+                         .abbr = 'A',
+                         .name = "accel",
+                         .expr_name = "<measure-accel-error>",
+                         .desc = "Measure co-variance for acceleration" },
+               },
+
+               .unknown = &user_argind,
+       };
+
+       ParseArgs::parseargs(&argd, &argv);
+
+       if (compute != "none") {
+               parameters_t    param;
+
+               printf ("/* Kalman matrix for %s\n", compute);
+               printf (" *     step = %f\n", time_step);
+               printf (" *     σ_m = %f\n", σ_m);
+               switch (compute) {
+               case "both":
+                       printf (" *     σ_h = %f\n", σ_h);
+                       printf (" *     σ_a = %f\n", σ_a);
+                       param = param_both(time_step, σ_m, σ_h, σ_a);
+                       break;
+               case "accel":
+                       printf (" *     σ_a = %f\n", σ_a);
+                       param = param_accel(time_step, σ_m, σ_a);
+                       break;
+               case "baro":
+                       printf (" *     σ_h = %f\n", σ_h);
+                       param = param_baro(time_step, σ_m, σ_h);
+                       break;
+               }
+               printf (" */\n\n");
+               mat_t k = converge(param);
+               int[] d = dims(k);
+               int time_inc = floor(1/time_step + 0.5);
+               for (int i = 0; i < d[0]; i++)
+                       for (int j = 0; j < d[1]; j++) {
+                               string name;
+                               if (d[1] == 1)
+                                       name = sprintf("%s_K%d_%d", prefix, i, time_inc);
+                               else
+                                       name = sprintf("%s_K%d%d_%d", prefix, i, j, time_inc);
+                               printf ("#define %s to_fix32(%12.10f)\n", name, k[i,j]);
+                       }
+               printf ("\n");
+               exit(0);
+       }
+       string[dim(argv) - user_argind] rest = { [i] = argv[i+user_argind] };
+
+       #       height_scale = accel_scale = speed_scale = 1;
+
+       if (dim(rest) == 0)
+               run_flight("<stdin>", stdin, summary);
+       else {
+               for (int i = 0; i < dim(rest); i++) {
+                       twixt(file f = File::open(rest[i], "r"); File::close(f)) {
+                               run_flight(rest[i], f, summary);
+                       }
+               }
+       }
+}
+main();
+#kalman(stdin);
+#dump(stdin);
diff --git a/src/kalman/kalman_filter.5c b/src/kalman/kalman_filter.5c
new file mode 100644 (file)
index 0000000..efbbf1a
--- /dev/null
@@ -0,0 +1,308 @@
+load "matrix.5c"
+
+/*
+ * Copyright © 2011 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.
+ */
+
+namespace kalman {
+
+       import matrix;
+
+       public typedef struct {
+               vec_t   x;              /* state */
+               mat_t   p;              /* error estimate */
+               mat_t   k;              /* kalman factor */
+       } state_t;
+
+       public typedef struct {
+               mat_t   a;              /* model */
+               mat_t   q;              /* model error covariance */
+               mat_t   r;              /* measurement error covariance */
+               mat_t   h;              /* measurement from model */
+       } parameters_t;
+
+       public typedef struct {
+               mat_t   a;              /* model */
+               mat_t   k;              /* kalman coefficient */
+               mat_t   h;              /* measurement from model */
+       } parameters_fast_t;
+
+       vec_t measurement_from_state(vec_t x, mat_t h) {
+               return multiply_mat_vec(h, x);
+       }
+
+
+       void print_state(string name, state_t s) {
+               print_vec(sprintf("%s state", name), s.x);
+               print_mat(sprintf("%s error", name), s.p);
+       }
+
+       public bool debug = false;
+
+       public state_t predict (state_t s, parameters_t p) {
+               state_t n;
+
+               if (debug) {
+                       printf ("--------PREDICT--------\n");
+                       print_state("current", s);
+               }
+
+               /* Predict state
+                *
+                * x': predicted state
+                * a:  model
+                * x:  previous state
+                *
+                * x' = a * x;
+                */
+
+               n.x = multiply_mat_vec(p.a, s.x);
+
+               /* t0 = a * p */
+               mat_t t0 = multiply (p.a, s.p);
+               if (debug)
+                       print_mat("t0", t0);
+
+               /* t1 = a * p * transpose(a) */
+
+               mat_t t1 = multiply (t0, transpose(p.a));
+
+               /* Predict error
+                *
+                * p': predicted error
+                * a:  model
+                * p:  previous error
+                * q:  model error
+                *
+                * p' = a * p * transpose(a) + q
+                */
+
+               n.p = add(t1, p.q);
+               if (debug)
+                       print_state("predict", n);
+               return n;
+       }
+
+       public vec_t predict_fast(vec_t x, parameters_fast_t p) {
+               if (debug) {
+                       printf ("--------FAST PREDICT--------\n");
+                       print_vec("current", x);
+               }
+               vec_t new = multiply_mat_vec(p.a, x);
+               if (debug)
+                       print_vec("predict", new);
+               return new;
+       }
+
+       public vec_t correct_fast(vec_t x, vec_t z, parameters_fast_t p) {
+               if (debug) {
+                       printf ("--------FAST CORRECT--------\n");
+                       print_vec("measure", z);
+                       print_vec("current", x);
+               }
+               vec_t   model = multiply_mat_vec(p.h, x);
+               if (debug)
+                       print_vec("extract model", model);
+               vec_t   diff = vec_subtract(z, model);
+               if (debug)
+                       print_vec("difference", diff);
+               vec_t   adjust = multiply_mat_vec(p.k, diff);
+               if (debug)
+                       print_vec("adjust", adjust);
+
+               vec_t new = vec_add(x,
+                              multiply_mat_vec(p.k,
+                                               vec_subtract(z,
+                                                            multiply_mat_vec(p.h, x))));
+               if (debug)
+                       print_vec("correct", new);
+               return new;
+       }
+
+       public state_t correct(state_t s, vec_t z, parameters_t p) {
+               state_t n;
+
+               if (debug) {
+                       printf ("--------CORRECT--------\n");
+                       print_vec("measure", z);
+                       print_state("current", s);
+               }
+
+               /* t0 = p * T(h) */
+
+               /* 3x2 = 3x3 * 3x2 */
+               mat_t t0 = multiply(s.p, transpose(p.h));
+               if (debug)
+                       print_mat("t0", t0);
+
+               /* t1 = h * p */
+
+               /* 2x3 = 2x3 * 3x3 */
+               mat_t t1 = multiply(p.h, s.p);
+               if (debug)
+                       print_mat("t1", t1);
+
+               /* t2 = h * p * transpose(h) */
+
+               /* 2x2 = 2x3 * 3x2 */
+               mat_t t2 = multiply(t1, transpose(p.h));
+               if (debug)
+                       print_mat("t2", t2);
+
+               /* t3 = h * p * transpose(h) + r */
+
+               /* 2x2 = 2x2 + 2x2 */
+               mat_t t3 = add(t2, p.r);
+               if (debug)
+                       print_mat("t3", t3);
+
+               /* t4 = inverse(h * p * transpose(h) + r) */
+
+               /* 2x2 = 2x2 */
+               mat_t t4 = inverse(t3);
+               if (debug)
+                       print_mat("t4", t4);
+
+               /* Kalman value */
+
+               /* k: Kalman value
+                * p: error estimate
+                * h: state to measurement matrix
+                * r: measurement error covariance
+                *
+                * k = p * transpose(h) * inverse(h * p * transpose(h) + r)
+                *
+                * k = K(p)
+                */
+
+               /* 3x2 = 3x2 * 2x2 */
+               mat_t k = multiply(t0, t4);
+               if (debug)
+                       print_mat("k", k);
+               n.k = k;
+
+               /* t5 = h * x */
+
+               /* 2 = 2x3 * 3 */
+               vec_t t5 = multiply_mat_vec(p.h, s.x);
+               if (debug)
+                       print_vec("t5", t5);
+
+               /* t6 = z - h * x */
+
+               /* 2 = 2 - 2 */
+               vec_t t6 = vec_subtract(z, t5);
+               if (debug)
+                       print_vec("t6", t6);
+
+               /* t7 = k * (z - h * x) */
+
+               /* 3 = 3x2 * 2 */
+               vec_t t7 = multiply_mat_vec(k, t6);
+               if (debug)
+                       print_vec("t7", t7);
+
+               /* Correct state
+                *
+                * x:  predicted state
+                * k:  kalman value
+                * z:  measurement
+                * h:  state to measurement matrix
+                * x': corrected state
+                *
+                * x' = x + k * (z - h * x)
+                */
+
+               n.x = vec_add(s.x, t7);
+               if (debug)
+                       print_vec("n->x", n.x);
+
+               /* t8 = k * h */
+
+               /* 3x3 = 3x2 * 2x3 */
+               mat_t t8 = multiply(k, p.h);
+               if (debug)
+                       print_mat("t8", t8);
+
+               /* t9 = 1 - k * h */
+
+               /* 3x3 = 3x3 - 3x3 */
+               mat_t t9 = subtract(identity(dim(s.x)), t8);
+               if (debug)
+                       print_mat("t9", t9);
+
+               /* Correct error
+                *
+                * p:  predicted error
+                * k:  kalman value
+                * h:  state to measurement matrix
+                * p': corrected error
+                *
+                * p' = (1 - k * h) * p
+                *
+                * p' = P(k,p)
+                */
+
+               /* 3x3 = 3x3 * 3x3 */
+               n.p = multiply(t9, s.p);
+               if (debug) {
+                       print_mat("n->p", n.p);
+#                      print_state("correct", n);
+               }
+               return n;
+       }
+
+       real distance(mat_t a, mat_t b) {
+               int[2]  d = dims(a);
+               int     i_max = d[0];
+               int     j_max = d[1];
+               real    s = 0;
+
+               for (int i = 0; i < i_max; i++)
+                       for (int j = 0; j < j_max; j++)
+                               s += (a[i,j] - b[i,j]) ** 2;
+               return sqrt(s);
+       }
+
+       public mat_t converge(parameters_t p) {
+               int     model = dims(p.a)[0];
+               int     measure = dims(p.r)[0];
+               int     reps = 0;
+               state_t s = {
+                       .x = (real[model]) { 0 ... },
+                       .p = (real[model,model]) { { 0 ... } ... },
+                       .k = (real[model,measure]) { { 0 ... } ... }
+               };
+
+               vec_t   z = (real [measure]) { 0 ... };
+               for (;;) {
+                       state_t s_pre = predict(s, p);
+                       state_t s_post = correct(s_pre, z, p);
+                       real    d = distance(s.k, s_post.k);
+                       s = s_post;
+                       reps++;
+                       if (d < 1e-10 && reps > 10)
+                               break;
+               }
+               return s.k;
+       }
+
+       public parameters_fast_t convert_to_fast(parameters_t p) {
+               return (parameters_fast_t) {
+                       .a = p.a, .k = converge(p), .h = p.h
+               };
+       }
+}
diff --git a/src/kalman/load_csv.5c b/src/kalman/load_csv.5c
new file mode 100644 (file)
index 0000000..15e8316
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+namespace load_csv {
+       string[*] parse_data(file f) {
+               while (!File::end(f)) {
+                       string l = File::fgets(f);
+                       if (l[0] == '#')
+                               continue;
+                       return String::parse_csv(l);
+               }
+               return (string[0]) {};
+       }
+
+       public typedef struct {
+               bool    done;
+               real    time;
+               real    height;
+               real    acceleration;
+       } record_t;
+
+       public record_t parse_record(file f, real accel_scale) {
+               string[*] data = parse_data(f);
+               if (dim(data) == 0)
+                       return (record_t) { .done = true };
+               int time_off = 4;
+               int height_off = 11;
+               int accel_off = 8;
+               if (string_to_integer(data[0]) == 2) {
+                       time_off = 4;
+                       accel_off = 9;
+                       height_off = 12;
+               }
+               return (record_t) {
+                       .done = false,
+                               .time = string_to_real(data[time_off]),
+                               .height = imprecise(string_to_real(data[height_off])),
+                               .acceleration = imprecise(string_to_real(data[accel_off]) * accel_scale) };
+       }
+
+       public void dump(file f) {
+               for (;;) {
+                       record_t        r = parse_record(f, 1);
+                       if (r.done)
+                               break;
+                       printf ("%f %f %f\n", r.time, r.height, r.acceleration);
+               }
+       }
+}
diff --git a/src/kalman/matrix.5c b/src/kalman/matrix.5c
new file mode 100644 (file)
index 0000000..667648f
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright © 2011 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.
+ */
+
+namespace matrix {
+       public typedef real[*] vec_t;
+       public typedef real[*,*] mat_t;
+
+       public mat_t transpose(mat_t m) {
+               int[2] d = dims(m);
+               return (real[d[1],d[0]]) { [i,j] = m[j,i] };
+       }
+
+       public void print_mat(string name, mat_t m) {
+               int[2]  d = dims(m);
+               printf ("%s {\n", name);
+               for (int y = 0; y < d[0]; y++) {
+                       for (int x = 0; x < d[1]; x++)
+                               printf (" %14.8f", m[y,x]);
+                       printf ("\n");
+               }
+               printf ("}\n");
+       }
+
+       public void print_vec(string name, vec_t v) {
+               int d = dim(v);
+               printf ("%s {", name);
+               for (int x = 0; x < d; x++)
+                       printf (" %14.8f", v[x]);
+               printf (" }\n");
+       }
+
+       public mat_t multiply(mat_t a, mat_t b) {
+               int[2] da = dims(a);
+               int[2] db = dims(b);
+
+               assert(da[1] == db[0], "invalid matrix dimensions");
+
+               real dot(int rx, int ry) {
+                       real    r = 0.0;
+                       for (int i = 0; i < da[1]; i++)
+                               r += a[ry, i] * b[i, rx];
+                       return imprecise(r);
+               }
+
+               mat_t r = (real[da[0], db[1]]) { [ry,rx] = dot(rx,ry) };
+               return r;
+       }
+
+       public mat_t multiply_mat_val(mat_t m, real value) {
+               int[2] d = dims(m);
+               for (int j = 0; j < d[1]; j++)
+                       for (int i = 0; i < d[0]; i++)
+                               m[i,j] *= value;
+               return m;
+       }
+
+       public mat_t add(mat_t a, mat_t b) {
+               int[2]  da = dims(a);
+               int[2]  db = dims(b);
+
+               assert(da[0] == db[0] && da[1] == db[1], "mismatching dim in plus");
+               return (real[da[0], da[1]]) { [y,x] = a[y,x] + b[y,x] };
+       }
+
+       public mat_t subtract(mat_t a, mat_t b) {
+               int[2]  da = dims(a);
+               int[2]  db = dims(b);
+
+               assert(da[0] == db[0] && da[1] == db[1], "mismatching dim in minus");
+               return (real[da[0], da[1]]) { [y,x] = a[y,x] - b[y,x] };
+       }
+
+       public mat_t inverse(mat_t m) {
+               int[2] d = dims(m);
+
+               real[1,1] inverse_1(real[1,1] m) {
+                       return (real[1,1]) { { 1/m[0,0] } };
+               }
+
+               if (d[0] == 1 && d[1] == 1)
+                       return inverse_1(m);
+
+               real[2,2] inverse_2(real[2,2] m) {
+                       real    a = m[0,0], b = m[0,1];
+                       real    c = m[1,0], d = m[1,1];
+                       real det = a * d - b * c;
+                       return multiply_mat_val((real[2,2]) {
+                                       { d, -b }, { -c, a } }, 1/det);
+               }
+
+               if (d[0] == 2 && d[1] == 2)
+                       return inverse_2(m);
+
+               real[3,3] inverse_3(real[3,3] m) {
+                       real    a = m[0,0], b = m[0,1], c = m[0, 2];
+                       real    d = m[1,0], e = m[1,1], f = m[1, 2];
+                       real    g = m[2,0], h = m[2,1], k = m[2, 2];
+                       real    Z = a*(e*k-f*h) + b*(f*g - d*k) + c*(d*h-e*g);
+                       real    A = (e*k-f*h), B = (c*h-b*k), C=(b*f-c*e);
+                       real    D = (f*g-d*k), E = (a*k-c*g), F=(c*d-a*f);
+                       real    G = (d*h-e*g), H = (b*g-a*h), K=(a*e-b*d);
+                       return multiply_mat_val((real[3,3]) {
+                                       { A, B, C }, { D, E, F }, { G, H, K }},
+                               1/Z);
+               }
+
+               if (d[0] == 3 && d[1] == 3)
+                       return inverse_3(m);
+               assert(false, "cannot invert %v\n", d);
+               return m;
+       }
+
+       public mat_t identity(int d) {
+               return (real[d,d]) { [i,j] = (i == j) ? 1 : 0 };
+       }
+
+       public vec_t vec_subtract(vec_t a, vec_t b) {
+               int     da = dim(a);
+               int     db = dim(b);
+
+               assert(da == db, "mismatching dim in minus");
+               return (real[da]) { [x] = a[x] - b[x] };
+       }
+
+       public vec_t vec_add(vec_t a, vec_t b) {
+               int     da = dim(a);
+               int     db = dim(b);
+
+               assert(da == db, "mismatching dim in plus");
+               return (real[da]) { [x] = a[x] + b[x] };
+       }
+
+       public vec_t multiply_vec_mat(vec_t v, mat_t m) {
+               mat_t r2 = matrix::multiply((real[dim(v),1]) { [y,x] = v[y] }, m);
+               return (real[dim(v)]) { [y] = r2[y,0] };
+       }
+
+       public vec_t multiply_mat_vec(mat_t m, vec_t v) {
+               mat_t r2 = matrix::multiply(m, (real[dim(v), 1]) { [y,x] = v[y] });
+               int[2] d = dims(m);
+               return (real[d[0]]) { [y] = r2[y,0] };
+       }
+}
diff --git a/src/kalman/plotaccel b/src/kalman/plotaccel
new file mode 100644 (file)
index 0000000..fd54020
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/sh
+for i in "$@"; do
+gnuplot -p << EOF
+set title "$i"
+set ylabel "height (m)"
+set y2label "velocity (m/s), acceleration (m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+plot "$i" using 1:3 with lines lt 1 axes x1y2 title "raw speed",\
+     "$i" using 1:4 with lines lt 1 axes x1y2 title "raw accel",\
+     "$i" using 1:6 with lines lt 2 axes x1y2 title "both speed",\
+     "$i" using 1:7 with lines lt 2 axes x1y2 title "both accel",\
+     "$i" using 1:9 with lines lt 3 axes x1y2 title "accel speed",\
+     "$i" using 1:10 with lines lt 3 axes x1y2 title "accel accel"
+EOF
+done
diff --git a/src/kalman/plotkalman b/src/kalman/plotkalman
new file mode 100755 (executable)
index 0000000..d2041df
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+for i in "$@"; do
+gnuplot -p << EOF
+set title "$i"
+set ylabel "height (m)"
+set y2label "velocity (m/s), acceleration (m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+plot "$i" using 1:2 with lines lt 1 axes x1y1 title "raw height",\
+     "$i" using 1:3 with lines lt 1 axes x1y2 title "raw speed",\
+     "$i" using 1:4 with lines lt 1 axes x1y2 title "raw accel",\
+     "$i" using 1:5 with lines lt 2 axes x1y1 title "both height",\
+     "$i" using 1:6 with lines lt 2 axes x1y2 title "both vel",\
+     "$i" using 1:7 with lines lt 2 axes x1y2 title "both accel",\
+     "$i" using 1:8 with lines lt 3 axes x1y1 title "accel height",\
+     "$i" using 1:9 with lines lt 3 axes x1y2 title "accel vel",\
+     "$i" using 1:10 with lines lt 3 axes x1y2 title "accel accel",\
+     "$i" using 1:11 with lines lt 4 axes x1y1 title "baro height",\
+     "$i" using 1:12 with lines lt 4 axes x1y2 title "baro vel",\
+     "$i" using 1:13 with lines lt 4 axes x1y2 title "baro accel"
+EOF
+done
diff --git a/src/make-altitude b/src/make-altitude
deleted file mode 100644 (file)
index ac04e84..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-#!/usr/bin/nickle -f
-/*
- * Pressure Sensor Model, version 1.1
- *
- * written by Holly Grimes
- *
- * Uses the International Standard Atmosphere as described in
- *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
- *    from the Portland State Aerospace Society, except that the atmosphere
- *    is divided into layers with each layer having a different lapse rate.
- *
- * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
- *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
- *
- * Height measurements use the local tangent plane.  The postive z-direction is up.
- *
- * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
- *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
- *   in Joules/(kilogram-Kelvin).
- */
-
-const real GRAVITATIONAL_ACCELERATION = -9.80665;
-const real AIR_GAS_CONSTANT = 287.053;
-const int NUMBER_OF_LAYERS = 7;
-const real MAXIMUM_ALTITUDE = 84852;
-const real MINIMUM_PRESSURE = 0.3734;
-const real LAYER0_BASE_TEMPERATURE = 288.15;
-const real LAYER0_BASE_PRESSURE = 101325;
-
-/* lapse rate and base altitude for each layer in the atmosphere */
-const real[NUMBER_OF_LAYERS] lapse_rate = {
-       -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
-};
-const int[NUMBER_OF_LAYERS] base_altitude = {
-       0, 11000, 20000, 32000, 47000, 51000, 71000
-};
-
-
-/* outputs atmospheric pressure associated with the given altitude. altitudes
-   are measured with respect to the mean sea level */
-real altitude_to_pressure(real altitude) {
-
-   real base_temperature = LAYER0_BASE_TEMPERATURE;
-   real base_pressure = LAYER0_BASE_PRESSURE;
-
-   real pressure;
-   real base; /* base for function to determine pressure */
-   real exponent; /* exponent for function to determine pressure */
-   int layer_number; /* identifies layer in the atmosphere */
-   int delta_z; /* difference between two altitudes */
-
-   if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
-      return 0;
-
-   /* calculate the base temperature and pressure for the atmospheric layer
-      associated with the inputted altitude */
-   for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
-      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
-      if (lapse_rate[layer_number] == 0.0) {
-         exponent = GRAVITATIONAL_ACCELERATION * delta_z
-              / AIR_GAS_CONSTANT / base_temperature;
-         base_pressure *= exp(exponent);
-      }
-      else {
-         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
-         exponent = GRAVITATIONAL_ACCELERATION /
-              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
-         base_pressure *= pow(base, exponent);
-      }
-      base_temperature += delta_z * lapse_rate[layer_number];
-   }
-
-   /* calculate the pressure at the inputted altitude */
-   delta_z = altitude - base_altitude[layer_number];
-   if (lapse_rate[layer_number] == 0.0) {
-      exponent = GRAVITATIONAL_ACCELERATION * delta_z
-           / AIR_GAS_CONSTANT / base_temperature;
-      pressure = base_pressure * exp(exponent);
-   }
-   else {
-      base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
-      exponent = GRAVITATIONAL_ACCELERATION /
-           (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
-      pressure = base_pressure * pow(base, exponent);
-   }
-
-   return pressure;
-}
-
-
-/* outputs the altitude associated with the given pressure. the altitude
-   returned is measured with respect to the mean sea level */
-real pressure_to_altitude(real pressure) {
-
-   real next_base_temperature = LAYER0_BASE_TEMPERATURE;
-   real next_base_pressure = LAYER0_BASE_PRESSURE;
-
-   real altitude;
-   real base_pressure;
-   real base_temperature;
-   real base; /* base for function to determine base pressure of next layer */
-   real exponent; /* exponent for function to determine base pressure
-                             of next layer */
-   real coefficient;
-   int layer_number; /* identifies layer in the atmosphere */
-   int delta_z; /* difference between two altitudes */
-
-   if (pressure < 0)  /* illegal pressure */
-      return -1;
-   if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
-      return MAXIMUM_ALTITUDE;
-
-   /* calculate the base temperature and pressure for the atmospheric layer
-      associated with the inputted pressure. */
-   layer_number = -1;
-   do {
-      layer_number++;
-      base_pressure = next_base_pressure;
-      base_temperature = next_base_temperature;
-      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
-      if (lapse_rate[layer_number] == 0.0) {
-         exponent = GRAVITATIONAL_ACCELERATION * delta_z
-              / AIR_GAS_CONSTANT / base_temperature;
-         next_base_pressure *= exp(exponent);
-      }
-      else {
-         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
-         exponent = GRAVITATIONAL_ACCELERATION /
-              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
-         next_base_pressure *= pow(base, exponent);
-      }
-      next_base_temperature += delta_z * lapse_rate[layer_number];
-   }
-   while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
-
-   /* calculate the altitude associated with the inputted pressure */
-   if (lapse_rate[layer_number] == 0.0) {
-      coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
-                                                    * base_temperature;
-      altitude = base_altitude[layer_number]
-                    + coefficient * log(pressure / base_pressure);
-   }
-   else {
-      base = pressure / base_pressure;
-      exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
-                                       / GRAVITATIONAL_ACCELERATION;
-      coefficient = base_temperature / lapse_rate[layer_number];
-      altitude = base_altitude[layer_number]
-                      + coefficient * (pow(base, exponent) - 1);
-   }
-
-   return altitude;
-}
-
-real feet_to_meters(real feet)
-{
-    return feet * (12 * 2.54 / 100);
-}
-
-real meters_to_feet(real meters)
-{
-    return meters / (12 * 2.54 / 100);
-}
-
-/*
- * Values for our MP3H6115A pressure sensor
- *
- * From the data sheet:
- *
- * Pressure range: 15-115 kPa
- * Voltage at 115kPa: 2.82
- * Output scale: 27mV/kPa
- *
- *
- * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa
- * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa
- */
-
-real counts_per_kPa = 27 * 2047 / 3300;
-real counts_at_101_3kPa = 1674;
-
-real count_to_kPa(real count)
-{
-       return (count / 2047 + 0.095) / 0.009;
-}
-
-for (real count = 0; count <= 2047; count++) {
-       real    kPa = count_to_kPa(count);
-       real    meters = pressure_to_altitude(kPa * 1000);
-       printf ("       %d,     /* %6.2g kPa %d count */\n",
-               floor (meters + 0.5), kPa, count);
-}
diff --git a/src/megametrum-v0.1/.gitignore b/src/megametrum-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..b04d395
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+megametrum-*.elf
diff --git a/src/megametrum-v0.1/Makefile b/src/megametrum-v0.1/Makefile
new file mode 100644 (file)
index 0000000..a93f6f1
--- /dev/null
@@ -0,0 +1,118 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_companion.h \
+       ao_data.h \
+       ao_sample.h \
+       ao_pins.h \
+       altitude.h \
+       ao_kalman.h \
+       ao_product.h \
+       ao_ms5607.h \
+       ao_hmc5883.h \
+       ao_mpu6000.h \
+       ao_mma655x.h \
+       ao_cc1120_CC1120.h \
+       ao_profile.h \
+       ao_whiten.h \
+       stm32l.h
+
+#
+# Common AltOS sources
+#
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+#      ao_hmc5883.c
+
+ALTOS_SRC = \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_config.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_serial_stm.c \
+       ao_gps_skytraq.c \
+       ao_gps_report_mega.c \
+       ao_ignite.c \
+       ao_freq.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_cc1120.c \
+       ao_fec_tx.c \
+       ao_fec_rx.c \
+       ao_data.c \
+       ao_ms5607.c \
+       ao_mma655x.c \
+       ao_hmc5883.c \
+       ao_adc_stm.c \
+       ao_beep_stm.c \
+       ao_storage.c \
+       ao_m25.c \
+       ao_usb_stm.c \
+       ao_exti_stm.c \
+       ao_report.c \
+       ao_i2c_stm.c \
+       ao_mpu6000.c \
+       ao_convert_pa.c \
+       ao_log.c \
+       ao_log_mega.c \
+       ao_sample.c \
+       ao_kalman.c \
+       ao_flight.c \
+       ao_telemetry.c \
+       ao_packet_slave.c \
+       ao_packet.c \
+       ao_companion.c \
+       ao_pyro.c \
+       $(PROFILE)
+
+PRODUCT=MegaMetrum-v0.1
+PRODUCT_DEF=-DMEGAMETRUM
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g
+
+PROGNAME=megametrum-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+
+SRC=$(ALTOS_SRC) ao_megametrum.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ) altos.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+../altitude.h: make-altitude
+       nickle $< > $@
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/megametrum-v0.1/ao_megametrum.c b/src/megametrum-v0.1/ao_megametrum.c
new file mode 100644 (file)
index 0000000..d3ae469
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_hmc5883.h>
+#include <ao_mpu6000.h>
+#include <ao_mma655x.h>
+#include <ao_log.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#include <ao_pyro.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+       
+       ao_serial_init();
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_GREEN);
+       ao_timer_init();
+
+       ao_i2c_init();
+       ao_spi_init();
+       ao_dma_init();
+       ao_exti_init();
+
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+
+#if HAS_MS5607
+       ao_ms5607_init();
+#endif
+#if HAS_HMC5883
+       ao_hmc5883_init();
+#endif
+#if HAS_MPU6000
+       ao_mpu6000_init();
+#endif
+#if HAS_MMA655X
+       ao_mma655x_init();
+#endif
+
+       ao_storage_init();
+       
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+
+       ao_usb_init();
+       ao_gps_init();
+       ao_gps_report_mega_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(FALSE);
+       ao_igniter_init();
+       ao_companion_init();
+       ao_pyro_init();
+
+       ao_config_init();
+#if AO_PROFILE
+       ao_profile_init();
+#endif
+       
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/megametrum-v0.1/ao_pins.h b/src/megametrum-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..af8eeba
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* 8MHz High speed external crystal */
+#define AO_HSE                 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL              12
+#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV              3
+#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER      2
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER      2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     0
+#define SERIAL_1_PB6_PB7       0
+#define SERIAL_1_PA9_PA10      1
+
+#define HAS_SERIAL_2           0
+#define USE_SERIAL_2_STDIN     0
+#define SERIAL_2_PA2_PA3       0
+#define SERIAL_2_PD5_PD6       0
+
+#define HAS_SERIAL_3           1
+#define USE_SERIAL_3_STDIN     0
+#define SERIAL_3_PB10_PB11     0
+#define SERIAL_3_PC10_PC11     1
+#define SERIAL_3_PD8_PD9       0
+
+#define ao_gps_getchar         ao_serial3_getchar
+#define ao_gps_putchar         ao_serial3_putchar
+#define ao_gps_set_speed       ao_serial3_set_speed
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     0
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define HAS_RADIO              1
+#define HAS_TELEMETRY          1
+
+#define HAS_SPI_1              1
+#define SPI_1_PA5_PA6_PA7      1       /* Barometer */
+#define SPI_1_PB3_PB4_PB5      0
+#define SPI_1_PE13_PE14_PE15   1       /* Accelerometer */
+
+#define HAS_SPI_2              1
+#define SPI_2_PB13_PB14_PB15   1       /* Flash, Companion */
+#define SPI_2_PD1_PD3_PD4      0
+
+#define SPI_2_PORT             (&stm_gpiob)
+#define SPI_2_SCK_PIN          13
+#define SPI_2_MISO_PIN         14
+#define SPI_2_MOSI_PIN         15
+
+#define HAS_I2C_1              1
+#define I2C_1_PB8_PB9          1
+
+#define HAS_I2C_2              1
+#define I2C_2_PB10_PB11                1
+
+#define PACKET_HAS_SLAVE       1
+#define PACKET_HAS_MASTER      0
+
+#define LOW_LEVEL_DEBUG                0
+
+#define LED_PORT_ENABLE                STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT               (&stm_gpioc)
+#define LED_PIN_RED            8
+#define LED_PIN_GREEN          9
+#define AO_LED_RED             (1 << LED_PIN_RED)
+#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
+
+#define LEDS_AVAILABLE         (AO_LED_RED | AO_LED_GREEN)
+
+#define HAS_GPS                        1
+#define HAS_FLIGHT             1
+#define HAS_ADC                        1
+#define HAS_ACCEL              1
+#define HAS_ACCEL_REF          1
+#define HAS_LOG                        1
+
+/*
+ * Igniter
+ */
+
+#define HAS_IGNITE             1
+#define HAS_IGNITE_REPORT      1
+
+#define AO_SENSE_DROGUE(p)     ((p)->adc.sense[0])
+#define AO_SENSE_MAIN(p)       ((p)->adc.sense[1])
+#define AO_IGNITER_CLOSED      400
+#define AO_IGNITER_OPEN                60
+
+#define AO_IGNITER_DROGUE_PORT (&stm_gpiod)
+#define AO_IGNITER_DROGUE_PIN  6
+
+#define AO_IGNITER_MAIN_PORT   (&stm_gpiod)
+#define AO_IGNITER_MAIN_PIN    7
+
+#define AO_PYRO_PORT_0 (&stm_gpiob)
+#define AO_PYRO_PIN_0  5
+
+#define AO_PYRO_PORT_1 (&stm_gpioe)
+#define AO_PYRO_PIN_1  4
+
+#define AO_PYRO_PORT_2 (&stm_gpioe)
+#define AO_PYRO_PIN_2  6
+
+#define AO_PYRO_PORT_3 (&stm_gpioe)
+#define AO_PYRO_PIN_3  5
+
+/* Number of general purpose pyro channels available */
+#define AO_PYRO_NUM    4
+
+#define AO_IGNITER_SET_DROGUE(v)       stm_gpio_set(AO_IGNITER_DROGUE_PORT, AO_IGNITER_DROGUE_PIN, v)
+#define AO_IGNITER_SET_MAIN(v)         stm_gpio_set(AO_IGNITER_MAIN_PORT, AO_IGNITER_MAIN_PIN, v)
+
+/*
+ * ADC
+ */
+#define AO_DATA_RING           32
+#define AO_ADC_NUM_SENSE       6
+
+struct ao_adc {
+       int16_t                 sense[AO_ADC_NUM_SENSE];
+       int16_t                 v_batt;
+       int16_t                 v_pbatt;
+       int16_t                 accel_ref;
+       int16_t                 accel;
+       int16_t                 temp;
+};
+
+#define AO_ADC_SENSE_A         0
+#define AO_ADC_SENSE_A_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_A_PIN     0
+
+#define AO_ADC_SENSE_B         1
+#define AO_ADC_SENSE_B_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_B_PIN     1
+
+#define AO_ADC_SENSE_C         2
+#define AO_ADC_SENSE_C_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_C_PIN     2
+
+#define AO_ADC_SENSE_D         3
+#define AO_ADC_SENSE_D_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_D_PIN     3
+
+#define AO_ADC_SENSE_E         4
+#define AO_ADC_SENSE_E_PORT    (&stm_gpioa)
+#define AO_ADC_SENSE_E_PIN     4
+
+#define AO_ADC_SENSE_F         22
+#define AO_ADC_SENSE_F_PORT    (&stm_gpioe)
+#define AO_ADC_SENSE_F_PIN     7
+
+#define AO_ADC_V_BATT          8
+#define AO_ADC_V_BATT_PORT     (&stm_gpiob)
+#define AO_ADC_V_BATT_PIN      0
+
+#define AO_ADC_V_PBATT         9
+#define AO_ADC_V_PBATT_PORT    (&stm_gpiob)
+#define AO_ADC_V_PBATT_PIN     1
+
+#define AO_ADC_ACCEL_REF       10
+#define AO_ADC_ACCEL_REF_PORT  (&stm_gpioc)
+#define AO_ADC_ACCEL_REF_PIN   0
+
+#define AO_ADC_ACCEL           11
+#define AO_ADC_ACCEL_PORT      (&stm_gpioc)
+#define AO_ADC_ACCEL_PIN       1
+
+#define AO_ADC_TEMP            16
+
+#define AO_ADC_RCC_AHBENR      ((1 << STM_RCC_AHBENR_GPIOAEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOEEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOBEN) | \
+                                (1 << STM_RCC_AHBENR_GPIOCEN))
+
+#define AO_NUM_ADC_PIN         (AO_ADC_NUM_SENSE + 4)
+
+#define AO_ADC_PIN0_PORT       AO_ADC_SENSE_A_PORT
+#define AO_ADC_PIN0_PIN                AO_ADC_SENSE_A_PIN
+#define AO_ADC_PIN1_PORT       AO_ADC_SENSE_B_PORT
+#define AO_ADC_PIN1_PIN                AO_ADC_SENSE_B_PIN
+#define AO_ADC_PIN2_PORT       AO_ADC_SENSE_C_PORT
+#define AO_ADC_PIN2_PIN                AO_ADC_SENSE_C_PIN
+#define AO_ADC_PIN3_PORT       AO_ADC_SENSE_D_PORT
+#define AO_ADC_PIN3_PIN                AO_ADC_SENSE_D_PIN
+#define AO_ADC_PIN4_PORT       AO_ADC_SENSE_E_PORT
+#define AO_ADC_PIN4_PIN                AO_ADC_SENSE_E_PIN
+#define AO_ADC_PIN5_PORT       AO_ADC_SENSE_F_PORT
+#define AO_ADC_PIN5_PIN                AO_ADC_SENSE_F_PIN
+#define AO_ADC_PIN6_PORT       AO_ADC_V_BATT_PORT
+#define AO_ADC_PIN6_PIN                AO_ADC_V_BATT_PIN
+#define AO_ADC_PIN7_PORT       AO_ADC_V_PBATT_PORT
+#define AO_ADC_PIN7_PIN                AO_ADC_V_PBATT_PIN
+#define AO_ADC_PIN8_PORT       AO_ADC_ACCEL_REF_PORT
+#define AO_ADC_PIN8_PIN                AO_ADC_ACCEL_REF_PIN
+#define AO_ADC_PIN9_PORT       AO_ADC_ACCEL_PORT
+#define AO_ADC_PIN9_PIN                AO_ADC_ACCEL_PIN
+
+#define AO_NUM_ADC             (AO_ADC_NUM_SENSE + 5)
+
+#define AO_ADC_SQ1             AO_ADC_SENSE_A
+#define AO_ADC_SQ2             AO_ADC_SENSE_B
+#define AO_ADC_SQ3             AO_ADC_SENSE_C
+#define AO_ADC_SQ4             AO_ADC_SENSE_D
+#define AO_ADC_SQ5             AO_ADC_SENSE_E
+#define AO_ADC_SQ6             AO_ADC_SENSE_F
+#define AO_ADC_SQ7             AO_ADC_V_BATT
+#define AO_ADC_SQ8             AO_ADC_V_PBATT
+#define AO_ADC_SQ9             AO_ADC_ACCEL_REF
+#define AO_ADC_SQ10            AO_ADC_ACCEL
+#define AO_ADC_SQ11            AO_ADC_TEMP
+
+/*
+ * Pressure sensor settings
+ */
+#define HAS_MS5607             1
+#define AO_MS5607_PRIVATE_PINS 1
+#define AO_MS5607_CS_PORT      (&stm_gpioc)
+#define AO_MS5607_CS_PIN       4
+#define AO_MS5607_CS_MASK      (1 << AO_MS5607_CS)
+#define AO_MS5607_MISO_PORT    (&stm_gpioa)
+#define AO_MS5607_MISO_PIN     6
+#define AO_MS5607_MISO_MASK    (1 << AO_MS5607_MISO)
+#define AO_MS5607_SPI_INDEX    AO_SPI_1_PA5_PA6_PA7
+
+/*
+ * SPI Flash memory
+ */
+
+#define M25_MAX_CHIPS          1
+#define AO_M25_SPI_CS_PORT     (&stm_gpiod)
+#define AO_M25_SPI_CS_MASK     (1 << 3)
+#define AO_M25_SPI_BUS         AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Radio (cc1120)
+ */
+
+/* gets pretty close to 434.550 */
+
+#define AO_RADIO_CAL_DEFAULT   0x6ca333
+
+#define AO_FEC_DEBUG           0
+#define AO_CC1120_SPI_CS_PORT  (&stm_gpioc)
+#define AO_CC1120_SPI_CS_PIN   5
+#define AO_CC1120_SPI_BUS      AO_SPI_2_PB13_PB14_PB15
+
+#define AO_CC1120_INT_PORT     (&stm_gpioc)
+#define AO_CC1120_INT_PIN      14
+
+#define AO_CC1120_INT_GPIO     2
+#define HAS_BOOT_RADIO         1
+
+/*
+ * Mag sensor (hmc5883)
+ */
+
+#define HAS_HMC5883            0
+#define AO_HMC5883_INT_PORT    (&stm_gpioc)
+#define AO_HMC5883_INT_PIN     12
+#define AO_HMC5883_I2C_INDEX   STM_I2C_INDEX(1)
+
+/*
+ * mpu6000
+ */
+
+#define HAS_MPU6000            1       
+#define AO_MPU6000_INT_PORT    (&stm_gpioc)
+#define AO_MPU6000_INT_PIN     13
+#define AO_MPU6000_I2C_INDEX   STM_I2C_INDEX(1)
+
+#define HAS_HIGHG_ACCEL                0
+
+/*
+ * mma655x
+ */
+
+#define HAS_MMA655X            1
+#define AO_MMA655X_SPI_INDEX   AO_SPI_1_PE13_PE14_PE15
+#define AO_MMA655X_CS_PORT     (&stm_gpiod)
+#define AO_MMA655X_CS_PIN      4
+
+#define NUM_CMDS               16
+
+/*
+ * Companion
+ */
+
+#define AO_COMPANION_CS_PORT   (&stm_gpiod)
+#define AO_COMPANION_CS_PIN    (0)
+#define AO_COMPANION_SPI_BUS   AO_SPI_2_PB13_PB14_PB15
+
+/*
+ * Monitor
+ */
+
+#define HAS_MONITOR            0
+#define LEGACY_MONITOR         0
+#define HAS_MONITOR_PUT                1
+#define AO_MONITOR_LED         0
+#define HAS_RSSI               0
+
+/*
+ * Profiling Viterbi decoding
+ */
+
+#ifndef AO_PROFILE
+#define AO_PROFILE             0
+#endif
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/megametrum-v0.1/stlink-pins b/src/megametrum-v0.1/stlink-pins
new file mode 100644 (file)
index 0000000..e609437
--- /dev/null
@@ -0,0 +1,49 @@
+       ST discovery card pins
+
+1      AIN-1
+2      JTCK
+3      GND
+4      JTMS
+5      NRST
+6      SWO
+
+MM misc connector
+
+1      GND
+2      reset_n
+3      boot0
+4      tx1
+5      rx1
+6      +3.3V
+7      GND
+8      jtms
+9      jtck
+10     jtdi
+11     jtdo
+12     jntrst
+13     sda2
+14     scl2
+15     pe1
+16     pe0
+
+For debugging:
+
+       ST      MM
+JTCK   2       9
+GND    3       7
+JTMS   4       8
+NRST   5       2
+
+TL debug connector:
+
+       TL      ST
+GND    1       3
+NRST   2       5
+SWDIO  3       4
+SWCLK  4       2
+
+MegaAccel:
+
+Jumpers
+PC0 (pin15) (blue)     PE0 (pin97)     accel_ref       (debug 16)
+PC1 (pin16) (green)    PE1 (pin98)     accel           (debug 15)
diff --git a/src/product/Makefile.telebt b/src/product/Makefile.telebt
new file mode 100644 (file)
index 0000000..fd52cec
--- /dev/null
@@ -0,0 +1,97 @@
+#
+# TeleBT build file
+#
+# Define TELEBT_VER, TELEBT_DEF, TELEBT_INC and TELEBT_SRC
+# and include this file
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       ao_product.h \
+       $(TELEBT_INC)
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_gps_print.c \
+       ao_monitor.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_state.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_master.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC = \
+       ao_btm.c
+
+PRODUCT_SRC = \
+       ao_telebt.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC) \
+       $(TELEBT_SRC)
+
+PROGNAME = telebt-v$(TELEBT_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleBT-v$(TELEBT_VER)
+PRODUCT_DEF=-DTELEBT_V_$(TELEBT_DEF)
+IDPRODUCT=0x000e
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
diff --git a/src/product/Makefile.teledongle b/src/product/Makefile.teledongle
new file mode 100644 (file)
index 0000000..3101b77
--- /dev/null
@@ -0,0 +1,98 @@
+#
+# TeleDongle build file
+#
+# The various teledongle versions differ only
+# in minor pin variations
+# so the per-board makefiles simply define
+# TD_VER, TD_DEF and include
+# this file
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_gps_print.c \
+       ao_monitor.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_rssi.c \
+       ao_state.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_master.c \
+       ao_radio.c \
+       ao_send_packet.c \
+       ao_romconfig.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC =
+
+PRODUCT_SRC = \
+       ao_teledongle.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = teledongle-v$(TD_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleDongle-v$(TD_VER)
+PRODUCT_DEF=-DTELEDONGLE_V_$(TD_DEF)
+IDPRODUCT=0x000c
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
diff --git a/src/product/Makefile.telelaunch b/src/product/Makefile.telelaunch
new file mode 100644 (file)
index 0000000..1e55989
--- /dev/null
@@ -0,0 +1,100 @@
+#
+# TeleLaunch build file
+#
+# define TELELAUNCH_VER, TELELAUNCH_DEF
+# this file
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_convert.c \
+       ao_launch.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_adc.c \
+       ao_aes.c \
+       ao_beep.c \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_ignite.c \
+       ao_intflash.c \
+       ao_led.c \
+       ao_radio.c \
+       ao_radio_cmac.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_spi.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC =
+
+PRODUCT_SRC = \
+       ao_telelaunch.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = telelaunch-v$(TELELAUNCH_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleLaunch-v$(TELELAUNCH_VER)
+PRODUCT_DEF=-DTELELAUNCH_V_$(TELELAUNCH_DEF)
+IDPRODUCT=0x000f
+CODESIZE=0x6700
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM)  || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
diff --git a/src/product/Makefile.telemetrum b/src/product/Makefile.telemetrum
new file mode 100644 (file)
index 0000000..5e3eed7
--- /dev/null
@@ -0,0 +1,112 @@
+#
+# TeleMetrum build file
+#
+# The various telemetrum versions differ only
+# in which flash and GPS drivers are included,
+# so the per-board makefiles simply define
+# TM_VER, TM_DEF, TM_INC and TM_SRC and include
+# this file
+
+vpath %.c .:..:../core:../cc1111:../drivers:../product
+vpath %.h .:..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       altitude.h \
+       ao_kalman.h \
+       ao_product.h \
+       $(TM_INC)
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_convert.c \
+       ao_gps_report.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_task.c \
+       ao_flight.c \
+       ao_sample.c \
+       ao_kalman.c \
+       ao_log.c \
+       ao_log_big.c \
+       ao_report.c \
+       ao_telemetry.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_adc.c \
+       ao_beep.c \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_ignite.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_slave.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_string.c \
+       ao_spi.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC = \
+       $(TM_SRC)
+
+PRODUCT_SRC = \
+       ao_telemetrum.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = telemetrum-v$(TM_VER)$(TM_EXTRA)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleMetrum-v$(TM_VER)
+PRODUCT_DEF=-DTELEMETRUM_V_$(TM_DEF)
+IDPRODUCT=0x000b
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
diff --git a/src/product/Makefile.telemini b/src/product/Makefile.telemini
new file mode 100644 (file)
index 0000000..ef8906b
--- /dev/null
@@ -0,0 +1,101 @@
+#
+# TeleMini build file
+#
+# Define TELEMINI_VER and TELEMINI_DEF and then
+# include this file
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_convert.c \
+       ao_flight.c \
+       ao_kalman.c \
+       ao_log.c \
+       ao_log_tiny.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_report.c \
+       ao_sample.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_task.c \
+       ao_telemetry.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_adc.c \
+       ao_dma.c \
+       ao_ignite.c \
+       ao_intflash.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_slave.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_string.c \
+       ao_timer.c \
+       _bp.c
+
+DRIVER_SRC =
+
+PRODUCT_SRC = \
+       ao_telemini.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = telemini-v$(TELEMINI_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleMini-v$(TELEMINI_VER)
+PRODUCT_DEF=-DTELEMINI_V_$(TELEMINI_DEF)
+IDPRODUCT=0x000a
+CODESIZE=0x6700
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
diff --git a/src/product/Makefile.telenano b/src/product/Makefile.telenano
new file mode 100644 (file)
index 0000000..67410ae
--- /dev/null
@@ -0,0 +1,100 @@
+#
+# TeleNano build file
+#
+# Define TELENANO_VER and TELENANO_DEF and then
+# include this file
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_convert.c \
+       ao_flight_nano.c \
+       ao_kalman.c \
+       ao_log.c \
+       ao_log_tiny.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_report.c \
+       ao_sample.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_task.c \
+       ao_telemetry.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_adc.c \
+       ao_dma.c \
+       ao_intflash.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_slave.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_string.c \
+       ao_timer.c \
+       _bp.c
+
+DRIVER_SRC =
+
+PRODUCT_SRC = \
+       ao_telenano.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = telenano-v$(TELENANO_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleNano-v$(TELENANO_VER)
+PRODUCT_DEF=-DTELENANO_V_$(TELENANO_DEF)
+IDPRODUCT=0x000a
+CODESIZE=0x6700
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
diff --git a/src/product/ao_telebt.c b/src/product/ao_telebt.c
new file mode 100644 (file)
index 0000000..46c6341
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#if HAS_LOG
+__code uint8_t ao_log_format = AO_LOG_FORMAT_NONE;     /* until we actually log stuff */
+#endif
+
+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();
+#if HAS_BEEP
+       ao_beep_init();
+#endif
+       ao_cmd_init();
+#if HAS_EEPROM
+       ao_spi_init();
+       ao_storage_init();
+#endif
+       ao_usb_init();
+       ao_monitor_init();
+#if HAS_LOG
+       ao_report_init();
+#endif
+       ao_radio_init();
+       ao_packet_master_init();
+       ao_btm_init();
+#if HAS_LOG
+       ao_log_single_init();
+#endif
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+#if HAS_AES
+       ao_aes_init();
+       ao_radio_cmac_init();
+#endif
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_teledongle.c b/src/product/ao_teledongle.c
new file mode 100644 (file)
index 0000000..25ebe73
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_send_packet.h>
+
+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_rssi_init(AO_LED_RED);
+       ao_radio_init();
+       ao_packet_master_init();
+       ao_send_packet_init();
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_telelaunch.c b/src/product/ao_telelaunch.c
new file mode 100644 (file)
index 0000000..b6e4bfc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_pins.h"
+
+void
+main(void)
+{
+       ao_clock_init();
+
+       /* Turn on the red LED until the system is stable */
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+
+       ao_timer_init();
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_storage_init();
+       ao_usb_init();
+       ao_radio_init();
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+       ao_aes_init();
+       ao_launch_init();
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_telemetrum.c b/src/product/ao_telemetrum.c
new file mode 100644 (file)
index 0000000..ea77f5a
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_pins.h"
+
+void
+main(void)
+{
+       /*
+        * Reduce the transient on the ignite pins at startup by
+        * pulling the pins low as soon as possible at power up
+        */
+       ao_ignite_set_pins();
+
+       ao_clock_init();
+
+       /* Turn on the red LED until the system is stable */
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+
+       /* A hack -- look at the SPI clock pin, if it's sitting at
+        *  ground, then we force the computer to idle mode instead of
+        *  flight mode
+        */
+       if (P1_3 == 0) {
+               ao_flight_force_idle = 1;
+               while (P1_3 == 0)
+                       ;
+       }
+       ao_timer_init();
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_storage_init();
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+       ao_usb_init();
+       ao_serial_init();
+       ao_gps_init();
+       ao_gps_report_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(FALSE);
+       ao_igniter_init();
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+#if HAS_COMPANION
+       ao_companion_init();
+#endif
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_telemini.c b/src/product/ao_telemini.c
new file mode 100644 (file)
index 0000000..21551ee
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_pins.h"
+
+__xdata uint8_t ao_force_freq;
+
+void
+main(void)
+{
+       /*
+        * Reduce the transient on the ignite pins at startup by
+        * pulling the pins low as soon as possible at power up
+        */
+       ao_ignite_set_pins();
+
+       ao_clock_init();
+
+       /* Turn on the red LED until the system is stable */
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+
+       /* A hack -- look at the debug clock pin, if it's sitting at
+        *  ground, then we force the computer to idle mode instead of
+        *  flight mode
+        */
+       if (P2_2 == 0) {
+               ao_flight_force_idle = 1;
+               ao_force_freq = 1;
+               while (P2_2 == 0)
+                       ;
+       }
+
+       ao_timer_init();
+       ao_adc_init();
+       ao_cmd_init();
+       ao_storage_init();
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(TRUE);
+       ao_igniter_init();
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_telenano.c b/src/product/ao_telenano.c
new file mode 100644 (file)
index 0000000..d91983d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_pins.h"
+
+void
+main(void)
+{
+       ao_clock_init();
+
+
+       /* Turn on the red LED until the system is stable */
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+
+       ao_timer_init();
+       ao_adc_init();
+       ao_cmd_init();
+       ao_storage_init();
+       ao_flight_nano_init();
+       ao_log_init();
+       ao_report_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(TRUE);
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_telepyro.c b/src/product/ao_telepyro.c
new file mode 100644 (file)
index 0000000..79454fb
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       PORTE |= (1 << 6);
+       DDRE |= (1 << 6);
+
+       ao_avr_stdio_init();
+       ao_timer_init();
+       ao_cmd_init();
+       ao_spi_slave_init();
+       ao_usb_init();
+       ao_adc_init();
+       ao_storage_init();
+       ao_config_init();
+       ao_pyro_init();
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/product/ao_telescience.c b/src/product/ao_telescience.c
new file mode 100644 (file)
index 0000000..45b6d40
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       PORTE |= (1 << 6);
+       DDRE |= (1 << 6);
+
+       ao_avr_stdio_init();
+       ao_timer_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_spi_slave_init();
+       ao_storage_init();
+       ao_usb_init();
+       ao_adc_init();
+       ao_log_single_init();
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/product/ao_teleterra.c b/src/product/ao_teleterra.c
new file mode 100644 (file)
index 0000000..d696b91
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define AO_NO_ADC_ISR 1
+#include "ao.h"
+
+void
+main(void)
+{
+       ao_clock_init();
+
+       /* Turn on the red LED until the system is stable */
+       ao_led_init(AO_LED_RED|AO_LED_GREEN);
+       ao_led_on(AO_LED_RED);
+       ao_timer_init();
+       ao_beep_init();
+       ao_cmd_init();
+       ao_usb_init();
+       ao_serial_init();
+       ao_monitor_init(AO_LED_GREEN, TRUE);
+       ao_radio_init();
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_teleterra_0_2.c b/src/product/ao_teleterra_0_2.c
new file mode 100644 (file)
index 0000000..68f0259
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define AO_NO_ADC_ISR 1
+#include "ao.h"
+
+void
+main(void)
+{
+       ao_clock_init();
+
+       ao_timer_init();
+       ao_beep_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_storage_init();
+       ao_usb_init();
+       ao_serial_init();
+       ao_gps_init();
+       ao_monitor_init();
+       ao_report_init();
+       ao_log_single_init();
+       ao_radio_init();
+       ao_packet_master_init();
+       ao_config_init();
+       ao_dbg_init();
+       ao_lcd_init();
+       ao_terraui_init();
+       ao_button_init();
+       ao_battery_init();
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_terraui.c b/src/product/ao_terraui.c
new file mode 100644 (file)
index 0000000..1866eb0
--- /dev/null
@@ -0,0 +1,646 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_flight.h>
+#include <math.h>
+
+static __xdata struct ao_telemetry_sensor              ao_tel_sensor;
+static __xdata struct ao_telemetry_location            ao_tel_location;
+static __xdata struct ao_telemetry_configuration       ao_tel_config;
+static __xdata int16_t                                         ao_tel_max_speed;
+static __xdata int16_t                                 ao_tel_max_height;
+static int8_t ao_tel_rssi;
+
+static __xdata char ao_lcd_line[17];
+static __xdata char ao_state_name[] = "SIPBFCDMLI";
+
+static void
+ao_terraui_line(uint8_t addr)
+{
+       ao_lcd_goto(addr);
+       ao_lcd_putstring(ao_lcd_line);
+}
+
+#define ao_terraui_state()     (ao_state_name[ao_tel_sensor.state])
+
+static char
+ao_terraui_igniter(int16_t sense)
+{
+       if (sense < AO_IGNITER_OPEN)
+               return '-';
+       if (sense > AO_IGNITER_CLOSED)
+               return '+';
+       return '?';
+}
+
+static char
+ao_terraui_battery(void)
+{
+       if (ao_tel_sensor.v_batt > 25558)
+               return '+';
+       return '-';
+}
+
+static char
+ao_terraui_gps(void)
+{
+       if (ao_tel_location.flags & (1 << 4)) {
+               if ((ao_tel_location.flags & 0xf) >= 4)
+                       return '+';
+       }
+       return '-';
+}
+
+static char
+ao_terraui_local_gps(void)
+{
+       if (ao_gps_data.flags & (1 << 4)) {
+               if ((ao_gps_data.flags & 0xf) >= 4)
+                       return '+';
+       }
+       return '-';
+}
+
+static char
+ao_terraui_logging(void)
+{
+       if (ao_tel_config.flight != 0)
+               return '+';
+       return '-';
+}
+
+static __code char ao_progress[4] = { '\011', '\012', '\014', '\013' };
+
+static uint8_t ao_telem_progress;
+static uint8_t ao_gps_progress;
+
+static void
+ao_terraui_startup(void)
+{
+       sprintf(ao_lcd_line, "%-16.16s", ao_product);
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+       sprintf(ao_lcd_line, "%-8.8s %5u  ", ao_version, ao_serial_number);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static void
+ao_terraui_info_firstline(void)
+{
+       sprintf(ao_lcd_line, "S %4d %7.7s %c",
+               ao_tel_sensor.serial,
+               ao_tel_config.callsign,
+               ao_terraui_state());
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+}
+       
+static void
+ao_terraui_info(void)
+{
+       ao_terraui_info_firstline();
+       sprintf(ao_lcd_line, "F %4d RSSI%4d%c",
+               ao_tel_config.flight,
+               ao_tel_rssi,
+               ao_progress[ao_telem_progress]);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static void
+ao_terraui_pad(void)
+{
+       sprintf(ao_lcd_line, "B%c A%c M%c L%c G%c %c",
+               ao_terraui_battery(),
+               ao_terraui_igniter(ao_tel_sensor.sense_d),
+               ao_terraui_igniter(ao_tel_sensor.sense_m),
+               ao_terraui_logging(),
+               ao_terraui_gps(),
+               ao_terraui_state());
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+       sprintf(ao_lcd_line, "SAT %2d RSSI%4d%c",
+               ao_tel_location.flags & 0xf,
+               ao_tel_rssi,
+               ao_progress[ao_telem_progress]);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static void
+ao_terraui_ascent(void)
+{
+       sprintf(ao_lcd_line, "S %5d S\011%5d%c",
+               ao_tel_sensor.speed >> 4,
+               ao_tel_max_speed >> 4,
+               ao_terraui_state());
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+       sprintf(ao_lcd_line, "H %5d H\011%5d%c",
+               ao_tel_sensor.height >> 4,
+               ao_tel_max_height >> 4,
+               ao_progress[ao_telem_progress]);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static int16_t mag(int32_t d)
+{
+       if (d < 0)
+               d = -d;
+       if (d > 0x7fff)
+               d = 0x7fff;
+       return d;
+}
+
+static int32_t
+dist(int32_t d)
+{
+       __pdata uint32_t m;
+       uint8_t neg = 0;
+
+       if (d < 0) {
+               d = -d;
+               neg = 1;
+       }
+
+       m = 10000000;
+       while (d >= (2147483647 / 111198)) {
+               d /= 10;
+               m /= 10;
+       }
+       d = (d * 111198) / m;
+       if (neg)
+               d = -d;
+       return d;
+}
+
+static __code uint8_t cos_table[] = {
+   0, /*  0 */
+   0, /*  1 */
+   0, /*  2 */
+ 255, /*  3 */
+ 254, /*  4 */
+ 253, /*  5 */
+ 252, /*  6 */
+ 251, /*  7 */
+ 249, /*  8 */
+ 247, /*  9 */
+ 245, /* 10 */
+ 243, /* 11 */
+ 240, /* 12 */
+ 238, /* 13 */
+ 235, /* 14 */
+ 232, /* 15 */
+ 228, /* 16 */
+ 225, /* 17 */
+ 221, /* 18 */
+ 217, /* 19 */
+ 213, /* 20 */
+ 209, /* 21 */
+ 205, /* 22 */
+ 200, /* 23 */
+ 195, /* 24 */
+ 190, /* 25 */
+ 185, /* 26 */
+ 180, /* 27 */
+ 175, /* 28 */
+ 169, /* 29 */
+ 163, /* 30 */
+ 158, /* 31 */
+ 152, /* 32 */
+ 145, /* 33 */
+ 139, /* 34 */
+ 133, /* 35 */
+ 126, /* 36 */
+ 120, /* 37 */
+ 113, /* 38 */
+ 106, /* 39 */
+ 100, /* 40 */
+  93, /* 41 */
+  86, /* 42 */
+  79, /* 43 */
+  71, /* 44 */
+  64, /* 45 */
+  57, /* 46 */
+  49, /* 47 */
+  42, /* 48 */
+  35, /* 49 */
+  27, /* 50 */
+  20, /* 51 */
+  12, /* 52 */
+   5, /* 53 */
+   1, /* 54 */
+};
+
+static __code uint8_t tan_table[] = {
+    0, /*  0 */
+    4, /*  1 */
+    9, /*  2 */
+   13, /*  3 */
+   18, /*  4 */
+   22, /*  5 */
+   27, /*  6 */
+   31, /*  7 */
+   36, /*  8 */
+   41, /*  9 */
+   45, /* 10 */
+   50, /* 11 */
+   54, /* 12 */
+   59, /* 13 */
+   64, /* 14 */
+   69, /* 15 */
+   73, /* 16 */
+   78, /* 17 */
+   83, /* 18 */
+   88, /* 19 */
+   93, /* 20 */
+   98, /* 21 */
+  103, /* 22 */
+  109, /* 23 */
+  114, /* 24 */
+  119, /* 25 */
+  125, /* 26 */
+  130, /* 27 */
+  136, /* 28 */
+  142, /* 29 */
+  148, /* 30 */
+  154, /* 31 */
+  160, /* 32 */
+  166, /* 33 */
+  173, /* 34 */
+  179, /* 35 */
+  186, /* 36 */
+  193, /* 37 */
+  200, /* 38 */
+  207, /* 39 */
+  215, /* 40 */
+  223, /* 41 */
+  231, /* 42 */
+  239, /* 43 */
+  247, /* 44 */
+};
+       
+int16_t ao_atan2(int32_t dy, int32_t dx) __reentrant
+{
+       int8_t  m = 1;
+       int16_t a = 0;
+       uint8_t r;
+       int8_t  t;
+
+       if (dx == 0) {
+               if (dy > 0)
+                       return 90;
+               if (dy < 0)
+                       return -90;
+               return 0;
+       }
+
+       if (dx < 0) {
+               a = 180;
+               m = -m;
+               dx = -dx;
+       }
+
+       if (dy < 0) {
+               m = -m;
+               a = -a;
+               dy = -dy;
+       }
+
+       if (dy > dx) {
+               int32_t t;
+
+               t = dy; dy = dx; dx = t;
+               a = a + m * 90;
+               m = -m;
+       }
+
+       dy = dy << 8;
+       dy = (dy + (dx >> 1)) / dx;
+       r = dy;
+       for (t = 0; t < 44; t++)
+               if (tan_table[t] >= r)
+                       break;
+       return t * m + a;
+}
+
+static __pdata int32_t lon_dist, lat_dist;
+static __pdata uint32_t        ground_dist, range;
+static __pdata uint8_t dist_in_km;
+static __pdata int16_t bearing, elevation;
+
+static void
+ao_terraui_lat_dist(void)
+{
+       lat_dist = dist (ao_tel_location.latitude - ao_gps_data.latitude);
+}
+
+static void
+ao_terraui_lon_dist(void) __reentrant
+{
+       uint8_t c = cos_table[ao_gps_data.latitude >> 24];
+       lon_dist = ao_tel_location.longitude;
+
+       /* check if it's shorter to go the other way around */
+       if ((int16_t) (lon_dist >> 24) < (int16_t) (ao_gps_data.longitude >> 24) - (int16_t) (1800000000 >> 24))
+               lon_dist += 3600000000ul;
+       lon_dist = dist(lon_dist - ao_gps_data.longitude);
+       if (c) {
+               if (lon_dist & 0x7f800000)
+                       lon_dist = (lon_dist >> 8) * c;
+               else
+                       lon_dist = (lon_dist * (int16_t) c) >> 8;
+       }
+}
+
+static int32_t sqr(int32_t x) { return x * x; }
+
+static void
+ao_terraui_compute(void)
+{
+       uint16_t        h = ao_tel_sensor.height;
+
+       ao_terraui_lat_dist();
+       ao_terraui_lon_dist();
+       if (lat_dist > 32767 || lon_dist > 32767) {
+               dist_in_km = 1;
+               ground_dist = sqr(lat_dist/1000) + sqr(lon_dist/1000);
+               h = h/1000;
+       } else {
+               dist_in_km = 0;
+               ground_dist = sqr(lat_dist) + sqr(lon_dist);
+       }
+       ground_dist = ao_sqrt (ground_dist);
+       range = ao_sqrt(ground_dist * ground_dist + ao_tel_sensor.height * ao_tel_sensor.height);
+       bearing = ao_atan2(lon_dist, lat_dist);
+       if (bearing < 0)
+               bearing += 360;
+       elevation = ao_atan2(ao_tel_sensor.height, ground_dist);
+}
+
+static void
+ao_terraui_descent(void)
+{
+       ao_terraui_compute();
+       sprintf(ao_lcd_line, "\007 %4d  \005 %3d  %c",
+               bearing, elevation,
+               ao_terraui_state());
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+       sprintf(ao_lcd_line, "H:%5d S:%5d%c",
+               ao_tel_sensor.height, ao_tel_sensor.speed >> 4,
+               ao_progress[ao_telem_progress]);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static void
+ao_terraui_recover(void)
+{
+       ao_terraui_compute();
+       sprintf(ao_lcd_line, "\007 %4d  \005 %3d  %c",
+               bearing, elevation,
+               ao_terraui_state());
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+       sprintf(ao_lcd_line, "R%5ld%cRSSI%4d%c",
+               ground_dist, dist_in_km ? 'k' : ' ',
+               ao_tel_rssi,
+               ao_progress[ao_telem_progress]);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static void
+ao_terraui_coord(int32_t c, char plus, char minus, char extra) __reentrant
+{
+       uint16_t        d;
+       uint8_t         m;
+       uint16_t        f;
+
+       if (c < 0) {
+               plus = minus;
+               c = -c;
+       }
+       d = c / 10000000;
+       c = c % 10000000;
+       c = c * 60;
+       m = c / 10000000;
+       c = c % 10000000;
+       f = (c + 500) / 1000;
+       sprintf(ao_lcd_line, "%c %3d\362 %2d.%04d\"%c",
+               plus, d, m, f, extra);
+}
+
+static void
+ao_terraui_remote(void)
+{
+       ao_terraui_coord(ao_tel_location.latitude, 'N', 'S', ao_terraui_state());
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+       ao_terraui_coord(ao_tel_location.longitude, 'E', 'W', ao_progress[ao_telem_progress]);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static void
+ao_terraui_local(void) __reentrant
+{
+       ao_terraui_coord(ao_gps_data.latitude, 'n', 's',
+                        ao_terraui_local_gps());
+       ao_terraui_line(AO_LCD_ADDR(0,0));
+       ao_terraui_coord(ao_gps_data.longitude, 'e', 'w', ao_progress[ao_gps_progress]);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+}
+
+static __pdata uint8_t ao_set_freq;
+static __pdata uint32_t ao_set_freq_orig;
+
+static void
+ao_terraui_freq(void) __reentrant
+{
+       uint16_t        MHz;
+       uint16_t        frac;
+       
+       MHz = ao_config.frequency / 1000;
+       frac = ao_config.frequency % 1000;
+       ao_terraui_info_firstline();
+       sprintf(ao_lcd_line, "Freq: %3d.%03d MHz", MHz, frac);
+       ao_terraui_line(AO_LCD_ADDR(1,0));
+       ao_lcd_goto(AO_LCD_ADDR(1,11));
+}
+
+static void
+ao_terraui_freq_start(void)
+{
+       ao_set_freq = 1;
+       ao_set_freq_orig = ao_config.frequency;
+       ao_lcd_cursor_on();
+}
+
+static void
+ao_terraui_freq_button(char b) {
+
+       switch (b) {
+       case 0:
+               return;
+       case 1:
+               if (ao_config.frequency > 430000)
+                       ao_config.frequency -= 100;
+               break;
+       case 2:
+               ao_set_freq = 0;
+               ao_lcd_cursor_off();
+               if (ao_set_freq_orig != ao_config.frequency)
+                       ao_config_put();
+               return;
+       case 3:
+               if (ao_config.frequency < 438000)
+                       ao_config.frequency += 100;
+               break;
+
+       }
+       ao_config_set_radio();
+       ao_radio_recv_abort();
+}
+
+static __code void (*__code ao_terraui_page[])(void) = {
+       ao_terraui_startup,
+       ao_terraui_info,
+       ao_terraui_pad,
+       ao_terraui_ascent,
+       ao_terraui_descent,
+       ao_terraui_recover,
+       ao_terraui_remote,
+       ao_terraui_local,
+};
+
+#define NUM_PAGE       (sizeof (ao_terraui_page)/sizeof (ao_terraui_page[0]))
+
+static __pdata uint8_t ao_current_page = 0;
+static __pdata uint8_t ao_shown_about = 3;
+
+static void
+ao_terraui(void)
+{
+       ao_lcd_start();
+
+       ao_delay(AO_MS_TO_TICKS(100));
+       ao_button_clear();
+
+       for (;;) {
+               char    b;
+
+               if (ao_set_freq)
+                       ao_terraui_freq();
+               else
+                       ao_terraui_page[ao_current_page]();
+
+               ao_alarm(AO_SEC_TO_TICKS(1));
+               b = ao_button_get();
+               ao_clear_alarm();
+
+               if (b > 0) {
+                       ao_beep_for(AO_BEEP_HIGH, AO_MS_TO_TICKS(10));
+                       ao_shown_about = 0;
+               }
+               
+               if (ao_set_freq) {
+                       ao_terraui_freq_button(b);
+                       continue;
+               }
+               switch (b) {
+               case 0:
+                       if (ao_shown_about) {
+                               if (--ao_shown_about == 0)
+                                       ao_current_page = 2;
+                       }
+                       break;
+               case 1:
+                       ao_current_page++;
+                       if (ao_current_page >= NUM_PAGE)
+                               ao_current_page = 0;
+                       break;
+               case 2:
+                       ao_terraui_freq_start();
+                       break;
+               case 3:
+                       if (ao_current_page == 0)
+                               ao_current_page = NUM_PAGE;
+                       ao_current_page--;
+                       break;
+               }
+       }
+}
+
+__xdata static struct ao_task ao_terraui_task;
+
+static void
+ao_terramonitor(void)
+{
+       uint8_t monitor;
+
+       monitor = ao_monitor_head;
+       ao_monitor_set(sizeof (struct ao_telemetry_generic));
+       for (monitor = ao_monitor_head;;
+            monitor = ao_monitor_ring_next(monitor))
+       {
+               while (monitor == ao_monitor_head)
+                       ao_sleep(DATA_TO_XDATA(&ao_monitor_head));
+               if (ao_monitoring != sizeof (union ao_telemetry_all))
+                       continue;
+               if (!(ao_monitor_ring[monitor].all.status & PKT_APPEND_STATUS_1_CRC_OK))
+                       continue;
+               ao_tel_rssi = AO_RSSI_FROM_RADIO(ao_monitor_ring[monitor].all.rssi);
+               switch (ao_monitor_ring[monitor].all.telemetry.generic.type) {
+               case AO_TELEMETRY_SENSOR_TELEMETRUM:
+               case AO_TELEMETRY_SENSOR_TELEMINI:
+               case AO_TELEMETRY_SENSOR_TELENANO:
+                       ao_xmemcpy(&ao_tel_sensor, &ao_monitor_ring[monitor], sizeof (ao_tel_sensor));
+                       if (ao_tel_sensor.state < ao_flight_boost) {
+                               ao_tel_max_speed = 0;
+                               ao_tel_max_height = 0;
+                       } else {
+                               if (ao_tel_sensor.speed > ao_tel_max_speed)
+                                       ao_tel_max_speed = ao_tel_sensor.speed;
+                               if (ao_tel_sensor.height > ao_tel_max_height)
+                                       ao_tel_max_height = ao_tel_sensor.height;
+                       }
+                       ao_telem_progress = (ao_telem_progress + 1) & 0x3;
+                       break;
+               case AO_TELEMETRY_LOCATION:
+                       ao_xmemcpy(&ao_tel_location, &ao_monitor_ring[monitor], sizeof (ao_tel_location));
+                       break;
+               case AO_TELEMETRY_CONFIGURATION:
+                       ao_xmemcpy(&ao_tel_config, &ao_monitor_ring[monitor], sizeof (ao_tel_config));
+               }
+       }
+}
+
+__xdata static struct ao_task ao_terramonitor_task;
+
+static void
+ao_terragps(void)
+{
+       uint16_t        gps_tick = ao_gps_progress;
+
+       for (;;) {
+               while (ao_gps_tick == gps_tick)
+                       ao_sleep(&ao_gps_data);
+               gps_tick = ao_gps_tick;
+               ao_gps_progress = (ao_gps_progress + 1) & 3;
+       }
+}
+
+__xdata static struct ao_task ao_terragps_task;
+
+void
+ao_terraui_init(void)
+{
+       ao_add_task(&ao_terraui_task, ao_terraui, "ui");
+       ao_add_task(&ao_terramonitor_task, ao_terramonitor, "monitor");
+       ao_add_task(&ao_terragps_task, ao_terragps, "gps");
+}
diff --git a/src/product/ao_test.c b/src/product/ao_test.c
new file mode 100644 (file)
index 0000000..14c2eb7
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+struct ao_task __xdata blink_0_task;
+struct ao_task __xdata blink_1_task;
+struct ao_task __xdata wakeup_task;
+struct ao_task __xdata beep_task;
+struct ao_task __xdata echo_task;
+
+void delay(int n) __reentrant
+{
+       uint8_t j = 0;
+       while (--n)
+               while (--j)
+                       ao_yield();
+}
+
+static __xdata uint8_t blink_chan;
+
+void
+blink_0(void)
+{
+       uint8_t b = 0;
+       for (;;) {
+               b = 1 - b;
+               if (b)
+                       ao_led_on(AO_LED_GREEN);
+               else
+                       ao_led_off(AO_LED_GREEN);
+               ao_sleep(&blink_chan);
+       }
+}
+
+void
+blink_1(void)
+{
+       static __xdata struct ao_adc adc;
+
+       for (;;) {
+               ao_sleep(&ao_adc_head);
+               ao_adc_get(&adc);
+               if (adc.accel < 15900)
+                       ao_led_on(AO_LED_RED);
+               else
+                       ao_led_off(AO_LED_RED);
+       }
+}
+
+void
+wakeup(void)
+{
+       for (;;) {
+               ao_delay(AO_MS_TO_TICKS(100));
+               ao_wakeup(&blink_chan);
+       }
+}
+
+void
+beep(void)
+{
+       static __xdata struct ao_adc adc;
+
+       for (;;) {
+               ao_delay(AO_SEC_TO_TICKS(1));
+               ao_adc_get(&adc);
+               if (adc.temp > 7400)
+                       ao_beep_for(AO_BEEP_LOW, AO_MS_TO_TICKS(50));
+       }
+}
+
+void
+echo(void)
+{
+       char    c;
+       for (;;) {
+               ao_usb_flush();
+               c = ao_usb_getchar();
+               ao_usb_putchar(c);
+               if (c == '\r')
+                       ao_usb_putchar('\n');
+       }
+}
+
+void
+main(void)
+{
+       ao_clock_init();
+
+//     ao_add_task(&blink_0_task, blink_0);
+//     ao_add_task(&blink_1_task, blink_1);
+//     ao_add_task(&wakeup_task, wakeup);
+//     ao_add_task(&beep_task, beep);
+       ao_add_task(&echo_task, echo);
+       ao_timer_init();
+       ao_adc_init();
+       ao_beep_init();
+       ao_led_init();
+       ao_usb_init();
+
+       ao_start_scheduler();
+}
diff --git a/src/product/ao_tidongle.c b/src/product/ao_tidongle.c
new file mode 100644 (file)
index 0000000..cba0b12
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define AO_NO_SERIAL_ISR 1
+#define AO_NO_ADC_ISR 1
+#include "ao.h"
+
+void
+main(void)
+{
+       ao_clock_init();
+
+       /* Turn on the LED until the system is stable */
+       ao_led_init(AO_LED_RED);
+       ao_led_on(AO_LED_RED);
+       ao_timer_init();
+       ao_cmd_init();
+       ao_usb_init();
+       ao_monitor_init();
+       ao_rssi_init(AO_LED_RED);
+       ao_radio_init();
+       ao_dbg_init();
+       ao_config_init();
+       /* Bring up the USB link */
+       P1DIR |= 1;
+       P1 |= 1;
+       ao_start_scheduler();
+}
diff --git a/src/sirf-cksum b/src/sirf-cksum
deleted file mode 100755 (executable)
index b905f31..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/usr/bin/env nickle
-
-int checksum(int[] msg)
-{
-       int sum = 0;
-       for (int i = 0; i < dim(msg); i++) {
-               sum += msg[i];
-               sum &= 0x7fff;
-       }
-       return sum;
-}
-
-void main()
-{
-       string[...]     input;
-       int[...]        msg;
-
-       setdim(input, 0);
-       while (!File::end(stdin)) {
-               input[dim(input)] = gets();
-       }
-
-       setdim(msg, 0);
-       for (int i = 0; i < dim(input); i++) {
-               string[*] words = String::wordsplit(input[i], " ,\t");
-               for (int j = 0; j < dim(words); j++) {
-                       if (words[j] == "/" + "*")
-                               break;
-                       if (String::length(words[j]) > 0 &&
-                           Ctype::isdigit(words[j][0])) {
-                               msg[dim(msg)] = string_to_integer(words[j]);
-                       }
-                }
-       }
-       printf("\t0xa0, 0xa2, 0x%02x, 0x%02x,\t/* length: %d bytes */\n",
-              dim(msg) >> 8, dim(msg) & 0xff, dim(msg));
-       for (int i = 0; i < dim(input); i++)
-               printf("%s\n", input[i]);
-       int csum = checksum(msg);
-       printf ("\t0x%02x, 0x%02x, 0xb0, 0xb3,\n",
-               csum >> 8, csum & 0xff);
-}
-
-main();
diff --git a/src/spiradio-v0.1/.gitignore b/src/spiradio-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..8e39138
--- /dev/null
@@ -0,0 +1,2 @@
+spiradio-*
+ao_product.h
diff --git a/src/spiradio-v0.1/.sdcdbrc b/src/spiradio-v0.1/.sdcdbrc
new file mode 100644 (file)
index 0000000..b9f6129
--- /dev/null
@@ -0,0 +1,2 @@
+--directory=../cc1111:../product:../core:../drivers:.
+
diff --git a/src/spiradio-v0.1/Makefile b/src/spiradio-v0.1/Makefile
new file mode 100644 (file)
index 0000000..a207d34
--- /dev/null
@@ -0,0 +1,92 @@
+#
+# SpiRadio build file
+#
+
+SPIRADIO_VER=0.1
+SPIRADIO_DEF=0_1
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       cc1111.h \
+       ao_product.h \
+       ao_radio_spi.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_aes.c \
+       ao_dma.c \
+       ao_led.c \
+       ao_radio.c \
+       ao_radio_cmac.c \
+       ao_radio_slave.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_spi.c \
+       ao_string.c \
+       ao_timer.c \
+       _bp.c
+
+PRODUCT_SRC = \
+       ao_spiradio.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = spiradio-v$(SPIRADIO_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=SpiRadio-v$(SPIRADIO_VER)
+PRODUCT_DEF=-DSPIRADIO_V_$(SPIRADIO_DEF)
+IDPRODUCT=0x000f
+CODESIZE=0x6700
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM)  || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
+
diff --git a/src/spiradio-v0.1/ao_pins.h b/src/spiradio-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..cf01c9c
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_RADIO              1
+
+#define HAS_FLIGHT             0
+#define HAS_USB                        0
+#define HAS_BEEP               0
+#define HAS_GPS                        0
+#define HAS_SERIAL_0           0
+#define HAS_SERIAL_0_ALT_1     0
+#define HAS_SERIAL_0_HW_FLOW   0
+#define USE_SERIAL_0_STDIN     0
+#define HAS_SERIAL_1           1
+#define HAS_SERIAL_1_ALT_1     1
+#define HAS_SERIAL_1_HW_FLOW   0
+#define USE_SERIAL_1_STDIN     1
+#define DELAY_SERIAL_1_STDIN   0
+#define HAS_ADC                        0
+#define HAS_DBG                        0
+#define HAS_EEPROM             0
+#define HAS_LOG                        0
+#define USE_INTERNAL_FLASH     0
+#define DBG_ON_P1              0
+#define PACKET_HAS_MASTER      0
+#define PACKET_HAS_SLAVE       0
+#define AO_LED_TX              1
+#define AO_LED_RX              2
+#define AO_LED_RED             AO_LED_TX
+#define LEDS_AVAILABLE         (AO_LED_TX|AO_LED_RX)
+#define HAS_EXTERNAL_TEMP      0
+#define HAS_ACCEL_REF          0
+#define SPI_CS_ON_P1           1
+#define HAS_AES                        1
+
+#define SPI_CS_PORT            P1
+#define SPI_CS_SEL             P1SEL
+#define SPI_CS_DIR             P1DIR
+#define AO_SPI_SLAVE           1
+#define HAS_SPI_0              1
+#define SPI_0_ALT_2            1
+#define HAS_SPI_1              0
+
+#define AO_RADIO_SLAVE_INT_PORT        P1
+#define AO_RADIO_SLAVE_INT_BIT 6
+#define AO_RADIO_SLAVE_INT_PIN P1_6
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/spiradio-v0.1/ao_spiradio.c b/src/spiradio-v0.1/ao_spiradio.c
new file mode 100644 (file)
index 0000000..d3647cc
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_radio_spi.h>
+
+void
+main(void)
+{
+       ao_clock_init();
+
+       ao_led_init(LEDS_AVAILABLE);
+
+       ao_serial_init();
+       ao_timer_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_radio_init();
+       ao_aes_init();
+       ao_config_init();
+       ao_radio_slave_init();
+       ao_start_scheduler();
+}
diff --git a/src/stm-bringup/.gitignore b/src/stm-bringup/.gitignore
new file mode 100644 (file)
index 0000000..5099253
--- /dev/null
@@ -0,0 +1 @@
+*.elf
diff --git a/src/stm-bringup/Makefile b/src/stm-bringup/Makefile
new file mode 100644 (file)
index 0000000..d45e836
--- /dev/null
@@ -0,0 +1,46 @@
+vpath % ..:../core:../product:../drivers:../stm
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+CC=arm-none-eabi-gcc
+OBJCOPY=arm-none-eabi-objcopy
+
+PDCLIB=/home/keithp/sat
+C_LIB=$(PDCLIB)/lib/pdclib.a
+C_INC=-I$(PDCLIB)/include
+
+DEF_CFLAGS=-g -std=gnu99 -Os -mlittle-endian -mthumb -ffreestanding -nostdlib -I../../src/stm $(C_INC)
+
+# to run from SRAM
+LD_FLAGS_RAM=-L../stm -Wl,-Taltos-ram.ld
+LD_FLAGS=-L../stm -Wl,-Tbringup.ld
+
+CFLAGS=$(DEF_CFLAGS) -mcpu=cortex-m3 -DCONFIG_STM32L_DISCOVERY
+
+SRC=bringup.c ao_interrupt.c
+OBJ=$(SRC:.c=.o)
+
+all: bringup-ram.elf bringup.elf
+
+%.bin: %.elf
+       $(OBJCOPY) -O binary $^ $@
+
+bringup.elf: $(OBJ) $(C_LIB) bringup.ld
+       $(CC) $(CFLAGS) $(LD_FLAGS) -o $@ $(OBJ) $(C_LIB) -lgcc
+
+bringup-ram.elf: $(OBJ) $(C_LIB) altos-ram.ld
+       $(CC) $(CFLAGS) $(LD_FLAGS_RAM) -o $@ $(OBJ) $(C_LIB) -lgcc
+
+clean:
+       rm -f *.o
+       rm -rf *.elf
+       rm -rf *.bin
+
+.PHONY: all clean
+
+install:
+
+uninstall:
diff --git a/src/stm-bringup/bringup.c b/src/stm-bringup/bringup.c
new file mode 100644 (file)
index 0000000..c5fad4a
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <string.h>
+
+#include <stdio.h>
+#include "stm32l.h"
+
+void delay(void);
+
+static void
+set_clock(void)
+{
+       uint32_t        cfgr;
+       uint32_t        cr;
+       
+       /* Set flash latency to tolerate 32MHz SYSCLK  -> 1 wait state */
+       uint32_t        acr = stm_flash.acr;
+
+       /* Enable 64-bit access and prefetch */
+       acr |= (1 << STM_FLASH_ACR_ACC64) | (1 << STM_FLASH_ACR_PRFEN);
+       stm_flash.acr = acr;
+
+       /* Enable 1 wait state so the CPU can run at 32MHz */
+       /* (haven't managed to run the CPU at 32MHz yet, it's at 16MHz) */
+       acr |= (1 << STM_FLASH_ACR_LATENCY);
+       stm_flash.acr = acr;
+
+       /* HCLK to 16MHz -> AHB prescaler = /1 */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
+       cfgr |= (STM_RCC_CFGR_HPRE_DIV_1 << STM_RCC_CFGR_HPRE);
+       stm_rcc.cfgr = cfgr;
+       while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
+              (STM_RCC_CFGR_HPRE_DIV_1 << STM_RCC_CFGR_HPRE))
+               asm ("nop");
+#define STM_AHB_PRESCALER      1
+
+       /* PCLK1 to 16MHz -> APB1 Prescaler = 1 */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE1_MASK << STM_RCC_CFGR_PPRE1);
+       cfgr |= (STM_RCC_CFGR_PPRE1_DIV_1 << STM_RCC_CFGR_PPRE1);
+       stm_rcc.cfgr = cfgr;
+#define STM_APB1_PRESCALER     1
+
+       /* PCLK2 to 16MHz -> APB2 Prescaler = 1 */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE2_MASK << STM_RCC_CFGR_PPRE2);
+       cfgr |= (STM_RCC_CFGR_PPRE2_DIV_1 << STM_RCC_CFGR_PPRE2);
+       stm_rcc.cfgr = cfgr;
+#define STM_APB2_PRESCALER     1
+
+       /* Enable power interface clock */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+       /* Set voltage range to 1.8V */
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+
+       /* Configure voltage scaling range */
+       cr = stm_pwr.cr;
+       cr &= ~(STM_PWR_CR_VOS_MASK << STM_PWR_CR_VOS);
+       cr |= (STM_PWR_CR_VOS_1_8 << STM_PWR_CR_VOS);
+       stm_pwr.cr = cr;
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+
+       /* Enable HSI RC clock 16MHz */
+       if (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY))) {
+               stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+               while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+                       asm("nop");
+       }
+#define STM_HSI 16000000
+
+       /* Switch to direct HSI for SYSCLK */
+       if ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+           (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS)) {
+               cfgr = stm_rcc.cfgr;
+               cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+               cfgr |= (STM_RCC_CFGR_SW_HSI << STM_RCC_CFGR_SW);
+               stm_rcc.cfgr = cfgr;
+               while ((stm_rcc.cfgr & (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS)) !=
+                      (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS))
+                       asm("nop");
+       }
+
+       /* Disable the PLL */
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_PLLON);
+       while (stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY))
+               asm("nop");
+       
+       /* PLLVCO to 96MHz (for USB) -> PLLMUL = 6, PLLDIV = 4 */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PLLMUL_MASK << STM_RCC_CFGR_PLLMUL);
+       cfgr &= ~(STM_RCC_CFGR_PLLDIV_MASK << STM_RCC_CFGR_PLLDIV);
+
+//     cfgr |= (STM_RCC_CFGR_PLLMUL_6 << STM_RCC_CFGR_PLLMUL);
+//     cfgr |= (STM_RCC_CFGR_PLLDIV_3 << STM_RCC_CFGR_PLLDIV);
+
+       cfgr |= (STM_RCC_CFGR_PLLMUL_6 << STM_RCC_CFGR_PLLMUL);
+       cfgr |= (STM_RCC_CFGR_PLLDIV_4 << STM_RCC_CFGR_PLLDIV);
+
+#define STM_PLLMUL     6
+#define STM_PLLDIV     4
+
+       /* PLL source to HSI */
+       cfgr &= ~(1 << STM_RCC_CFGR_PLLSRC);
+
+#define STM_PLLSRC     STM_HSI
+
+       stm_rcc.cfgr = cfgr;
+
+       /* Enable the PLL and wait for it */
+       stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
+               asm("nop");
+
+       /* Switch to the PLL for the system clock */
+
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+       cfgr |= (STM_RCC_CFGR_SW_PLL << STM_RCC_CFGR_SW);
+       stm_rcc.cfgr = cfgr;
+       for (;;) {
+               uint32_t        c, part, mask, val;
+
+               c = stm_rcc.cfgr;
+               mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+               val = (STM_RCC_CFGR_SWS_PLL << STM_RCC_CFGR_SWS);
+               part = c & mask;
+               if (part == val)
+                       break;
+       }
+}
+
+#define STM_PLLVCO     (STM_PLLSRC * STM_PLLMUL)
+#define STM_SYSCLK     (STM_PLLVCO / STM_PLLDIV)
+#define STM_HCLK       (STM_SYSCLK / STM_AHB_PRESCALER)
+#define STM_APB1       (STM_HCLK / STM_APB1_PRESCALER)
+#define STM_APB2       (STM_HCLK / STM_APB2_PRESCALER)
+
+#define BAUD_9600 (STM_APB2 / 9600)
+
+void
+set_serial()
+{
+       uint32_t        moder, afr;
+
+       /* Enable GPIOA */
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+       
+       /* Hook PA9, PA10 to USART1 (AFIO7) */
+       stm_moder_set(&stm_gpioa, 9, STM_MODER_ALTERNATE);
+       stm_moder_set(&stm_gpioa, 10, STM_MODER_ALTERNATE);
+       stm_afr_set(&stm_gpioa, 9, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioa, 10, STM_AFR_AF7);
+
+       /* Enable USART1 */
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+       
+       /* 9.6KBps. PCLK1 = 16MHz. OVER8 = 0 */
+
+       /* USARTDIV = PCLK1 / (16 * 9600) = 104.1{6}
+        * round to 104.1875 (1667 / 16)
+        *
+        * actual baud rate = 16e6 / (16 * 104.1875) = 9598Bps
+        */
+
+       stm_usart1.brr = BAUD_9600;
+
+       stm_usart1.cr1 = ((0 << STM_USART_CR1_OVER8) |
+                         (1 << STM_USART_CR1_UE) |
+                         (0 << STM_USART_CR1_M) |
+                         (0 << STM_USART_CR1_WAKE) |
+                         (0 << STM_USART_CR1_PCE) |
+                         (0 << STM_USART_CR1_PS) |
+                         (0 << STM_USART_CR1_PEIE) |
+                         (0 << STM_USART_CR1_TXEIE) |
+                         (0 << STM_USART_CR1_TCIE) |
+                         (0 << STM_USART_CR1_RXNEIE) |
+                         (0 << STM_USART_CR1_IDLEIE) |
+                         (1 << STM_USART_CR1_TE) |
+                         (1 << STM_USART_CR1_RE) |
+                         (0 << STM_USART_CR1_RWU) |
+                         (0 << STM_USART_CR1_SBK));
+
+       stm_usart1.cr2 = ((0 << STM_USART_CR2_LINEN) |
+                         (STM_USART_CR2_STOP_1 << STM_USART_CR2_STOP) |
+                         (0 << STM_USART_CR2_CLKEN) |
+                         (0 << STM_USART_CR2_CPOL) |
+                         (0 << STM_USART_CR2_CPHA) |
+                         (0 << STM_USART_CR2_LBCL) |
+                         (0 << STM_USART_CR2_LBDIE) |
+                         (0 << STM_USART_CR2_LBDL) |
+                         (0 << STM_USART_CR2_ADD));
+
+       stm_usart1.cr3 = ((0 << STM_USART_CR3_ONEBITE) |
+                         (0 << STM_USART_CR3_CTSIE) |
+                         (0 << STM_USART_CR3_CTSE) |
+                         (0 << STM_USART_CR3_RTSE) |
+                         (0 << STM_USART_CR3_DMAT) |
+                         (0 << STM_USART_CR3_DMAR) |
+                         (0 << STM_USART_CR3_SCEN) |
+                         (0 << STM_USART_CR3_NACK) |
+                         (0 << STM_USART_CR3_HDSEL) |
+                         (0 << STM_USART_CR3_IRLP) |
+                         (0 << STM_USART_CR3_IREN) |
+                         (0 << STM_USART_CR3_EIE));
+}
+
+void
+outbyte(char c)
+{
+       if (c == '\n')
+               outbyte('\r');
+       while (!(stm_usart1.sr & (1 << STM_USART_SR_TXE)))
+               ;
+       stm_usart1.dr = c;
+}
+
+int putc( int c, FILE * stream ) {
+       outbyte(c);
+}
+
+void
+serial_string(char *string)
+{
+       char    c;
+
+       while (c = *string++)
+               outbyte(c);
+}
+
+volatile uint16_t      tick_count;
+
+void
+stm_tim6_isr(void)
+{
+       if (stm_tim6.sr & (1 << STM_TIM67_SR_UIF)) {
+               stm_tim6.sr = 0;
+               ++tick_count;
+       }
+}
+
+#define TIMER_10kHz    (STM_APB1 / 10000)
+
+void
+set_timer6(void)
+{
+       stm_nvic_set_enable(STM_ISR_TIM6_POS);
+       stm_nvic_set_priority(STM_ISR_TIM6_POS, 1);
+
+       /* Turn on timer 6 */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM6EN);
+
+       stm_tim6.psc = TIMER_10kHz;
+       stm_tim6.arr = 100;
+       stm_tim6.cnt = 0;
+
+       /* Enable update interrupt */
+       stm_tim6.dier = (1 << STM_TIM67_DIER_UIE);
+
+       /* Poke timer to reload values */
+       stm_tim6.egr |= (1 << STM_TIM67_EGR_UG);
+
+       stm_tim6.cr2 = (STM_TIM67_CR2_MMS_RESET << STM_TIM67_CR2_MMS);
+
+       /* And turn it on */
+       stm_tim6.cr1 = ((0 << STM_TIM67_CR1_ARPE) |
+                       (0 << STM_TIM67_CR1_OPM) |
+                       (1 << STM_TIM67_CR1_URS) |
+                       (0 << STM_TIM67_CR1_UDIS) |
+                       (1 << STM_TIM67_CR1_CEN));
+}
+
+void
+main (void)
+{
+       set_clock();
+       set_serial();
+       set_timer6();
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+       stm_moder_set(&stm_gpiob, 7, STM_MODER_OUTPUT);
+       stm_moder_set(&stm_gpiob, 6, STM_MODER_OUTPUT);
+       for (;;) {
+               stm_gpiob.odr = (1 << 7);
+               printf ("hello, ");
+               delay();
+               stm_gpiob.odr = (1 << 6);
+               printf ("world %d\n", tick_count);
+               delay();
+       }
+}
+
+void
+delay(void)
+{
+       int i;
+       for (i = 0; i < 1000000; i++)
+               __asm__ __volatile__ ("nop\n\t":::"memory");
+}
diff --git a/src/stm-bringup/bringup.ld b/src/stm-bringup/bringup.ld
new file mode 100644 (file)
index 0000000..10d50cd
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+MEMORY {
+       rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
+       ram (!w) : ORIGIN = 0x20000000, LENGTH = 16K
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+       /*
+        * Rom contents
+        */
+
+       .text ORIGIN(rom) : {
+               __text_start__ = .;
+               *(.interrupt)   /* Interrupt vectors */
+
+               *(.text*)       /* Executable code */
+               *(.rodata*)     /* Constants */
+       } > rom
+
+       .ARM.exidx : {
+               *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+               __text_end__ = .;
+       } > rom
+
+       . = ORIGIN(ram);
+       __data_start__ = .;
+
+       /* Data -- relocated to RAM, but written to ROM
+        */
+       .data : AT (ADDR(.ARM.exidx) + SIZEOF (.ARM.exidx)) {
+               *(.data)        /* initialized data */
+               __data_end__ = .;
+               __bss_start__ = .;
+       } >ram
+
+       .bss : {
+               *(.bss)
+               *(COMMON)
+               __bss_end__ = .;
+       } >ram
+
+       PROVIDE(__stack__ = ORIGIN(ram) + LENGTH(ram));
+       PROVIDE(end = .);
+
+}
+
+ENTRY(start);
+
+
diff --git a/src/stm-demo/.gitignore b/src/stm-demo/.gitignore
new file mode 100644 (file)
index 0000000..32b08ce
--- /dev/null
@@ -0,0 +1,2 @@
+stm-demo
+ao_product.h
diff --git a/src/stm-demo/Makefile b/src/stm-demo/Makefile
new file mode 100644 (file)
index 0000000..09c9c3c
--- /dev/null
@@ -0,0 +1,74 @@
+#
+# AltOS build
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_pins.h \
+       ao_product.h
+
+#
+# Common AltOS sources
+#
+ALTOS_SRC = \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_serial_stm.c \
+       ao_lcd_stm.c \
+       ao_lcd_font.c \
+       ao_mutex.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_adc_stm.c \
+       ao_data.c \
+       ao_i2c_stm.c \
+       ao_usb_stm.c \
+       ao_exti_stm.c \
+       ao_event.c \
+       ao_quadrature.c \
+       ao_button.c
+
+PRODUCT=StmDemo-v0.0
+PRODUCT_DEF=-DSTM_DEMO
+IDPRODUCT=0x000a
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) -g -O0
+
+PROG=stm-demo
+
+SRC=$(ALTOS_SRC) ao_demo.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+LDFLAGS=-L../stm -Wl,-Taltos.ld
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+$(OBJ): $(INC)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROG)
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/stm-demo/ao_demo.c b/src/stm-demo/ao_demo.c
new file mode 100644 (file)
index 0000000..fe7c69f
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_exti.h>
+#include <ao_event.h>
+#include <ao_quadrature.h>
+#include <ao_button.h>
+
+struct ao_task demo_task;
+
+static inline int min(int a, int b) { return a < b ? a : b; }
+
+void
+ao_demo(void)
+{
+       char    message[] = "Hello, Mike & Bdale --- ";
+       char    part[7];
+       int     i = 0;
+       int     len = sizeof(message) - 1;
+       int     first, second;
+
+       part[6] = '\0';
+       for (;;) {
+               ao_delay(AO_MS_TO_TICKS(150));
+               first = min(6, len - i);
+               second = 6 - first;
+               memcpy(part, message + i, first);
+               memcpy(part + first, message, second);
+               ao_lcd_font_string(part);
+               if (++i >= len)
+                       i = 0;
+       }
+}
+
+void _close() { }
+void _sbrk() { }
+void _isatty() { }
+void _lseek() { }
+void _exit () { }
+void _read () { }
+void _fstat() { }
+
+#define AO_DMA_TEST_INDEX      STM_DMA_INDEX(4)
+
+static void
+ao_dma_test(void) {
+       static char     src[20] = "hello, world";
+       static char     dst[20];
+       
+       dst[0] = '\0';
+       ao_dma_set_transfer(AO_DMA_TEST_INDEX, dst, src, 13,
+                           (1 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_LOW << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (1 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+       ao_dma_start(AO_DMA_TEST_INDEX);
+       ao_arch_critical(
+               while (!ao_dma_done[AO_DMA_TEST_INDEX])
+                       ao_sleep(&ao_dma_done[AO_DMA_TEST_INDEX]);
+               );
+       ao_dma_done_transfer(AO_DMA_TEST_INDEX);
+       printf ("copied %s\n", dst);
+}
+
+static void
+ao_spi_write(void) {
+       unsigned char   data[] = { 0x55, 0xaa, 0xff, 0x00 };
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               ao_spi_get(0, AO_SPI_SPEED_FAST);
+               stm_gpio_set(&stm_gpioc, 12, 0);
+               ao_spi_send(data, 4, 0);
+               stm_gpio_set(&stm_gpioc, 12, 1);
+               ao_spi_put(0);
+               printf(".");
+               flush();
+               ao_delay(100);
+       }
+}
+
+static void
+ao_spi_read(void) {
+       unsigned char   data[4];
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               ao_spi_get(0, AO_SPI_SPEED_FAST);
+               stm_gpio_set(&stm_gpioc, 12, 0);
+               ao_spi_recv(data, 4, 0);
+               stm_gpio_set(&stm_gpioc, 12, 1);
+               ao_spi_put(0);
+               printf(".");
+               flush();
+               ao_delay(100);
+       }
+}
+
+static void
+ao_i2c_write(void) {
+       unsigned char   data[] = { 0x55, 0xaa, 0xff, 0x00 };
+       int i;
+
+       for (i = 0; i < 10; i++) {
+               ao_i2c_get(0);
+               if (ao_i2c_start(0, 0x55))
+                       ao_i2c_send(data, 4, 0, TRUE);
+               else {
+                       printf ("i2c start failed\n");
+                       ao_i2c_put(0);
+                       break;
+               }
+               ao_i2c_put(0);
+               printf(".");
+               flush();
+               ao_delay(100);
+       }
+}
+
+static void
+ao_temp (void)
+{
+       struct ao_data  packet;
+       int temp;
+
+       ao_data_get(&packet);
+
+       /*
+        * r = (110 - 25) / (ts_cal_hot - ts_cal_cold)
+        * 25 + (110 - 25) * (temp - ts_cal_cold) / (ts_cal_hot - ts_cal_cold)
+        */
+       temp = 25 + (110 - 25) * (packet.adc.temp - stm_temp_cal.ts_cal_cold) / (stm_temp_cal.ts_cal_hot - stm_temp_cal.ts_cal_cold);
+       printf ("temp: %d\n", temp);
+}
+
+static void
+ao_event(void)
+{
+       struct ao_event event;
+
+       for (;;) {
+               flush();
+               ao_event_get(&event);
+               printf ("type %1d unit %1d tick %5u value %ld\n",
+                       event.type, event.unit, event.tick, event.value);
+               if (event.value == 100)
+                       break;
+       }
+
+}
+
+__code struct ao_cmds ao_demo_cmds[] = {
+       { ao_dma_test,  "D\0DMA test" },
+       { ao_spi_write, "W\0SPI write" },
+       { ao_spi_read, "R\0SPI read" },
+       { ao_i2c_write, "i\0I2C write" },
+       { ao_temp, "t\0Show temp" },
+       { ao_event, "e\0Monitor event queue" },
+       { 0, NULL }
+};
+
+int
+main(void)
+{
+       ao_clock_init();
+       
+       ao_serial_init();
+       ao_timer_init();
+       ao_dma_init();
+       ao_cmd_init();
+       ao_lcd_stm_init();
+//     ao_lcd_font_init();
+       ao_spi_init();
+       ao_i2c_init();
+       ao_exti_init();
+       ao_quadrature_init();
+       ao_button_init();
+
+       ao_timer_set_adc_interval(100);
+
+       ao_adc_init();
+       ao_usb_init();
+
+       ao_cmd_register(&ao_demo_cmds[0]);
+       
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/stm-demo/ao_pins.h b/src/stm-demo/ao_pins.h
new file mode 100644 (file)
index 0000000..c9c7446
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* Bridge SB17 on the board and use the MCO from the other chip */
+#define AO_HSE         8000000
+#define AO_HSE_BYPASS          1
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL              12
+#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz */
+#define AO_PLLDIV              3
+#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHZ (CPU clock) */
+#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at HCLK/1 */
+#define AO_APB1_PRESCALER      1
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+
+/* Run APB2 at HCLK/1 */
+#define AO_APB2_PRESCALER              1
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_1
+
+#define HAS_SERIAL_1           1
+#define USE_SERIAL_1_STDIN     1
+#define SERIAL_1_PB6_PB7       1
+#define SERIAL_1_PA9_PA10      0
+
+#define HAS_SERIAL_2           0
+#define USE_SERIAL_2_STDIN     1
+#define SERIAL_2_PA2_PA3       0
+#define SERIAL_2_PD5_PD6       1
+
+#define HAS_SERIAL_3           0
+#define USE_SERIAL_3_STDIN     1
+#define SERIAL_3_PB10_PB11     0
+#define SERIAL_3_PC10_PC11     0
+#define SERIAL_3_PD8_PD9       1
+
+#define HAS_SPI_1              1
+#define SPI_1_PB3_PB4_PB5      1
+
+#define HAS_SPI_2              0
+
+#define HAS_USB                        1
+#define HAS_BEEP               0
+#define PACKET_HAS_SLAVE       0
+
+#define LOW_LEVEL_DEBUG                1
+
+#define LED_PORT_ENABLE                STM_RCC_AHBENR_GPIOBEN
+#define LED_PORT               (&stm_gpiob)
+#define LED_PIN_GREEN          7
+#define LED_PIN_BLUE           6
+#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
+#define AO_LED_BLUE            (1 << LED_PIN_BLUE)
+
+#define AO_LED_RED             AO_LED_BLUE     /* a patent lie */
+
+#define LEDS_AVAILABLE         (AO_LED_BLUE | AO_LED_GREEN)
+
+#define AO_LCD_STM_SEG_ENABLED_0 (             \
+               (1 << 0) | /* PA1 */            \
+               (1 << 1) | /* PA2 */            \
+               (1 << 2) | /* PA3 */            \
+               (0 << 3) | /* PA6 */            \
+               (0 << 4) | /* PA7 */            \
+               (0 << 5) | /* PB0 */            \
+               (0 << 6) | /* PB1 */            \
+               (1 << 7) | /* PB3 */            \
+               (1 << 8) | /* PB4 */            \
+               (1 << 9) | /* PB5 */            \
+               (1 << 10) | /* PB10 */          \
+               (1 << 11) | /* PB11 */          \
+               (1 << 12) | /* PB12 */          \
+               (1 << 13) | /* PB13 */          \
+               (1 << 14) | /* PB14 */          \
+               (1 << 15) | /* PB15 */          \
+               (1 << 16) | /* PB8 */           \
+               (1 << 17) | /* PA15 */          \
+               (1 << 18) | /* PC0 */           \
+               (1 << 19) | /* PC1 */           \
+               (1 << 20) | /* PC2 */           \
+               (1 << 21) | /* PC3 */           \
+               (0 << 22) | /* PC4 */           \
+               (0 << 23) | /* PC5 */           \
+               (1 << 24) | /* PC6 */           \
+               (1 << 25) | /* PC7 */           \
+               (1 << 26) | /* PC8 */           \
+               (1 << 27) | /* PC9 */           \
+               (1 << 28) | /* PC10 or PD8 */   \
+               (1 << 29) | /* PC11 or PD9 */   \
+               (0 << 30) | /* PC12 or PD10 */  \
+               (0 << 31))  /* PD2 or PD11 */
+
+#define AO_LCD_STM_SEG_ENABLED_1 (             \
+               (0 << 0) | /* PD12 */           \
+               (0 << 1) | /* PD13 */           \
+               (0 << 2) | /* PD14 */           \
+               (0 << 3) | /* PD15 */           \
+               (0 << 4) | /* PE0 */            \
+               (0 << 5) | /* PE1 */            \
+               (0 << 6) | /* PE2 */            \
+               (0 << 7))  /* PE3 */
+
+#define AO_LCD_STM_COM_ENABLED (               \
+               (1 << 0) | /* PA8 */            \
+               (1 << 1) | /* PA9 */            \
+               (1 << 2) | /* PA10 */           \
+               (1 << 3) | /* PB9 */            \
+               (0 << 4) | /* PC10 */           \
+               (0 << 5) | /* PC11 */           \
+               (0 << 6)) /* PC12 */
+
+#define AO_LCD_28_ON_C 1
+
+#define AO_LCD_DUTY    STM_LCD_CR_DUTY_STATIC
+
+#define HAS_ADC                        1
+
+#define AO_ADC_RING            32
+
+struct ao_adc {
+       uint16_t                tick;
+       int16_t                 idd;
+       int16_t                 temp;
+       int16_t                 vref;
+};
+
+#define AO_ADC_IDD             4
+#define AO_ADC_PIN0_PORT       (&stm_gpioa)
+#define AO_ADC_PIN0_PIN                4
+
+#define AO_ADC_RCC_AHBENR      ((1 << STM_RCC_AHBENR_GPIOAEN))
+#define AO_ADC_TEMP            16
+#define AO_ADC_VREF            17
+
+#define HAS_ADC_TEMP           1
+
+#define AO_DATA_RING           32
+#define AO_NUM_ADC             3
+
+#define AO_ADC_SQ1             AO_ADC_IDD
+#define AO_ADC_SQ2             AO_ADC_TEMP
+#define AO_ADC_SQ3             AO_ADC_VREF
+       
+#define HAS_I2C_1              1
+#define I2C_1_PB6_PB7          0
+#define I2C_1_PB8_PB9          1
+
+#define HAS_I2C_2              0
+#define I2C_2_PB10_PB11                0
+
+#define AO_EVENT               1
+
+#define AO_QUADRATURE_COUNT    2
+#define AO_QUADRATURE_MODE     AO_EXTI_MODE_PULL_UP
+
+#define AO_QUADRATURE_0_PORT   &stm_gpioc
+#define AO_QUADRATURE_0_A      1
+#define AO_QUADRATURE_0_B      0
+
+#define AO_QUADRATURE_1_PORT   &stm_gpioc
+#define AO_QUADRATURE_1_A      3
+#define AO_QUADRATURE_1_B      2
+
+#define AO_BUTTON_COUNT                2
+#define AO_BUTTON_MODE         AO_EXTI_MODE_PULL_UP
+
+#define AO_BUTTON_0_PORT       &stm_gpioc
+#define AO_BUTTON_0            6
+
+#define AO_BUTTON_1_PORT       &stm_gpioc
+#define AO_BUTTON_1            7
+
+#define AO_TICK_TYPE           uint32_t
+#define AO_TICK_SIGNED         int32_t
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/stm/Makefile.defs b/src/stm/Makefile.defs
new file mode 100644 (file)
index 0000000..04404cd
--- /dev/null
@@ -0,0 +1,35 @@
+vpath % ../stm:../product:../drivers:../core:../util:../kalman:../aes:..
+vpath make-altitude ../util
+vpath make-kalman ../util
+vpath kalman.5c ../kalman
+vpath kalman_filter.5c ../kalman
+vpath load_csv.5c ../kalman
+vpath matrix.5c ../kalman
+vpath ao-make-product.5c ../util
+
+CC=arm-none-eabi-gcc
+SAT=/home/keithp/sat
+SAT_CLIB=$(SAT)/lib/pdclib.a
+SAT_CFLAGS=-I$(SAT)/include
+
+ifndef VERSION
+include ../Version
+endif
+
+AO_CFLAGS=-I. -I../stm -I../core -I../drivers -I..
+STM_CFLAGS=-std=gnu99 -mlittle-endian -mcpu=cortex-m3 -mthumb -ffreestanding -nostdlib $(AO_CFLAGS) $(SAT_CFLAGS)
+
+LDFLAGS=-L../stm -Wl,-Taltos.ld
+
+NICKLE=nickle
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+.c.o:
+       $(call quiet,CC) -c $(CFLAGS) -o $@ $<
diff --git a/src/stm/altos-ram.ld b/src/stm/altos-ram.ld
new file mode 100644 (file)
index 0000000..1143a08
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+MEMORY {
+       ram (rwx) : ORIGIN = 0x20000000, LENGTH = 16K
+}
+
+INCLUDE registers.ld
+
+SECTIONS {
+       . = ORIGIN(ram);
+
+       /*
+        * Rom contents
+        */
+
+       __text_start__ = .;
+
+       .text : {
+               *(.interrupt)   /* Interrupt vectors */
+               *(.text)        /* Executable code */
+               *(.rodata)      /* Constants */
+       } > ram
+
+       .ARM.exidx : {
+               *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+               __text_end__ = .;
+       } > ram
+
+       __data_start__ = .;
+
+       /* Data -- relocated to RAM, but written to ROM
+        */
+       .data : AT (ADDR(.ARM.exidx) + SIZEOF (.ARM.exidx)) {
+               *(.data)        /* initialized data */
+               __data_end__ = .;
+               __bss_start__ = .;
+       } >ram
+
+       .bss : {
+               *(.bss)
+               *(COMMON)
+               __bss_end__ = .;
+       } >ram
+
+       PROVIDE(__stack__ = ORIGIN(ram) + LENGTH(ram));
+       PROVIDE(end = .);
+
+}
+
+ENTRY(start);
+
+
diff --git a/src/stm/altos.ld b/src/stm/altos.ld
new file mode 100644 (file)
index 0000000..f78a45d
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+MEMORY {
+       rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K
+       ram (!w) : ORIGIN = 0x20000000, LENGTH = 16K
+}
+
+INCLUDE registers.ld
+
+EXTERN (stm_interrupt_vector)
+
+SECTIONS {
+       /*
+        * Rom contents
+        */
+
+       .text ORIGIN(rom) : {
+               __text_start__ = .;
+               *(.interrupt)   /* Interrupt vectors */
+
+               . = ORIGIN(rom) + 0x100;
+
+               ao_romconfig.o(.romconfig*)
+               ao_product.o(.romconfig*)
+
+               *(.text*)       /* Executable code */
+               *(.rodata*)     /* Constants */
+
+       } > rom
+
+       .ARM.exidx : {
+               *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+               __text_end__ = .;
+       } > rom
+
+       /* Data -- relocated to RAM, but written to ROM
+        */
+       .data ORIGIN(ram) : AT (ADDR(.ARM.exidx) + SIZEOF (.ARM.exidx)) {
+               __data_start__ = .;
+               *(.data)        /* initialized data */
+               __data_end__ = .;
+               __bss_start__ = .;
+       } >ram
+
+       .bss : {
+               *(.bss)
+               *(COMMON)
+               __bss_end__ = .;
+       } >ram
+
+       PROVIDE(__stack__ = ORIGIN(ram) + LENGTH(ram));
+       PROVIDE(end = .);
+}
+
+ENTRY(start);
+
+
diff --git a/src/stm/ao-parse-font.5c b/src/stm/ao-parse-font.5c
new file mode 100644 (file)
index 0000000..fe78585
--- /dev/null
@@ -0,0 +1,174 @@
+#!/usr/bin/env nickle
+#
+# Parse a 14-segment font file
+# and construct the bitmasks for each
+# character. Output is in the same
+# format as the input:
+#      [5] = 0x1212,
+# /*
+# CHAR 37 '%'
+#             
+#      |    / 
+#      |   /  
+#             
+#        /   |
+#       /    |
+#             
+# */
+#
+# Note that there can only be tabs before the glyph image
+# as spaces are significant in the image itself.
+#
+
+typedef struct {
+       int             c;
+       bool[14]        bits;
+} glyph;
+
+exception done();
+
+glyph read_glyph(file f) {
+       int     c;
+
+       for (;;) {
+               if (File::end(f))
+                       raise done();
+               string  l = File::fgets(f);
+               if (File::sscanf(l, "CHAR %d", &c) == 1)
+                       break;
+       }
+
+       string strip_tab(string x) {
+               int i = 0;
+               while (i < String::length(x) && x[i] == '\t')
+                       i++;
+               string n = String::substr(x, i, String::length(x) - i);
+               while (String::length(n) < 7)
+                       n = n + " ";
+               return n;
+       }
+
+       string[7] lines = { [i] = strip_tab(File::fgets(f)) };
+
+       glyph   g = { .c = c };
+
+       g.bits[0] = lines[0][1] == '-';
+
+       g.bits[1] = lines[1][0] == '|';
+       g.bits[2] = lines[1][1] == '\\';
+       g.bits[3] = lines[1][3] == '|';
+       g.bits[4] = lines[1][5] == '/';
+       g.bits[5] = lines[1][6] == '|';
+
+       g.bits[6] = lines[3][1] == '-';
+       g.bits[7] = lines[3][4] == '-';
+
+       g.bits[8] = lines[5][0] == '|';
+       g.bits[9] = lines[5][1] == '/';
+       g.bits[10] = lines[5][3] == '|';
+       g.bits[11] = lines[5][5] == '\\';
+       g.bits[12] = lines[5][6] == '|';
+
+       g.bits[13] = lines[6][1] == '-';
+       return g;
+}
+
+string[*] glyph_image(glyph g) {
+       int[7][7] chars = { { ' ' ... } ... };
+
+       if (g.bits[0])
+               for (int c = 1; c < 6; c++)
+                       chars[0][c] = '-';
+
+       if (g.bits[1])
+               for (int r = 1; r < 3; r++)
+                       chars[r][0] = '|';
+       if (g.bits[2])
+               for (int p = 1; p < 3; p++)
+                       chars[p][p] = '\\';
+       if (g.bits[3])
+               for (int p = 1; p < 3; p++)
+                       chars[p][3] = '|';
+       if (g.bits[4])
+               for (int p = 1; p < 3; p++)
+                       chars[p][6-p] = '/';
+       if (g.bits[5])
+               for (int p = 1; p < 3; p++)
+                       chars[p][6] = '|';
+
+       if (g.bits[6])
+               for (int p = 1; p < 3; p++)
+                       chars[3][p] = '-';
+       if (g.bits[7])
+               for (int p = 4; p < 6; p++)
+                       chars[3][p] = '-';
+
+       if (g.bits[8])
+               for (int r = 4; r < 6; r++)
+                       chars[r][0] = '|';
+       if (g.bits[9])
+               for (int p = 4; p < 6; p++)
+                       chars[p][6-p] = '/';
+       if (g.bits[10])
+               for (int p = 4; p < 6; p++)
+                       chars[p][3] = '|';
+       if (g.bits[11])
+               for (int p = 4; p < 6; p++)
+                       chars[p][p] = '\\';
+       if (g.bits[12])
+               for (int p = 4; p < 6; p++)
+                       chars[p][6] = '|';
+
+       if (g.bits[13])
+               for (int c = 1; c < 6; c++)
+                       chars[6][c] = '-';
+               
+       return (string[7]) { [i] = String::new(chars[i]) };
+
+}
+
+int glyph_value(glyph g) {
+       int     v = 0;
+
+       for (int b = 0; b < 14; b++)
+               if (g.bits[b])
+                       v |= (1 << b);
+       return v;
+}
+
+void write_glyph(file f, glyph g) {
+       File::fprintf (f, "CHAR %d '%s'\n", g.c, g.c == 127 ? "DEL" : String::new(g.c));
+       string[7] lines = glyph_image(g);
+       for (int i = 0; i < 7; i++)
+               File::fprintf (f, "\t%s\n", lines[i]);
+}
+
+autoload Sort;
+
+glyph[*] read_font(file f) {
+       glyph[128 - 32] font = { [i] = read_glyph(f) };
+
+       Sort::qsort(&font, bool func (glyph a, glyph b) = (a.c > b.c));
+       return font;
+}
+
+glyph[*] font;
+void init () {
+       twixt (file f = File::open("ao_lcd_font.h", "r"); File::close(f)) {
+               font = read_font(f);
+       }
+}
+
+void dump() {
+       twixt(file f = File::open("ao_lcd_font.h.new", "w"); File::close(f)) {
+               for (int i = 0; i < dim(font); i++) {
+                       File::fprintf (f, "\t[%d] = 0x%04x,\n", i, glyph_value(font[i]));
+                       File::fprintf (f, "/*\n");
+                       write_glyph(f, font[i]);
+                       File::fprintf (f, "*/\n\n");
+               }
+       }
+}
+
+init();
+dump();
diff --git a/src/stm/ao_adc_stm.c b/src/stm/ao_adc_stm.c
new file mode 100644 (file)
index 0000000..18ca6ea
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_data.h>
+
+static uint8_t                 ao_adc_ready;
+
+#define AO_ADC_CR2_VAL         ((0 << STM_ADC_CR2_SWSTART) |           \
+                                (STM_ADC_CR2_EXTEN_DISABLE << STM_ADC_CR2_EXTEN) | \
+                                (0 << STM_ADC_CR2_EXTSEL) |            \
+                                (0 << STM_ADC_CR2_JWSTART) |           \
+                                (STM_ADC_CR2_JEXTEN_DISABLE << STM_ADC_CR2_JEXTEN) | \
+                                (0 << STM_ADC_CR2_JEXTSEL) |           \
+                                (0 << STM_ADC_CR2_ALIGN) |             \
+                                (0 << STM_ADC_CR2_EOCS) |              \
+                                (1 << STM_ADC_CR2_DDS) |               \
+                                (1 << STM_ADC_CR2_DMA) |               \
+                                (STM_ADC_CR2_DELS_UNTIL_READ << STM_ADC_CR2_DELS) | \
+                                (0 << STM_ADC_CR2_CONT) |              \
+                                (1 << STM_ADC_CR2_ADON))
+
+/*
+ * Callback from DMA ISR
+ *
+ * Mark time in ring, shut down DMA engine
+ */
+static void ao_adc_done(int index)
+{
+       AO_DATA_PRESENT(AO_DATA_ADC);
+       ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+       ao_adc_ready = 1;
+}
+
+/*
+ * Start the ADC sequence using the DMA engine
+ */
+void
+ao_adc_poll(void)
+{
+       if (!ao_adc_ready)
+               return;
+       ao_adc_ready = 0;
+       stm_adc.sr = 0;
+       ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1),
+                           &stm_adc.dr,
+                           (void *) (&ao_data_ring[ao_data_head].adc),
+                           AO_NUM_ADC,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_16 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+       ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1), ao_adc_done);
+       ao_dma_start(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+       stm_adc.cr2 = AO_ADC_CR2_VAL | (1 << STM_ADC_CR2_SWSTART);
+}
+
+/*
+ * Fetch a copy of the most recent ADC data
+ */
+void
+ao_adc_get(__xdata struct ao_adc *packet)
+{
+#if HAS_FLIGHT
+       uint8_t i = ao_data_ring_prev(ao_sample_data);
+#else
+       uint8_t i = ao_data_ring_prev(ao_data_head);
+#endif
+       memcpy(packet, (void *) &ao_data_ring[i].adc, sizeof (struct ao_adc));
+}
+
+static void
+ao_adc_dump(void) __reentrant
+{
+       struct ao_data  packet;
+       int16_t *d;
+       uint8_t i;
+
+       ao_data_get(&packet);
+       printf("tick: %5u",  packet.tick);
+       d = (int16_t *) (&packet.adc);
+       for (i = 0; i < AO_NUM_ADC; i++)
+               printf (" %2d: %5d", i, d[i]);
+       printf("\n");
+}
+
+__code struct ao_cmds ao_adc_cmds[] = {
+       { ao_adc_dump,  "a\0Display current ADC values" },
+       { 0, NULL },
+};
+
+void
+ao_adc_init(void)
+{
+#ifdef AO_ADC_PIN0_PORT
+       stm_rcc.ahbenr |= AO_ADC_RCC_AHBENR;
+#endif
+
+#ifdef AO_ADC_PIN0_PORT
+       stm_moder_set(AO_ADC_PIN0_PORT, AO_ADC_PIN0_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN1_PORT
+       stm_moder_set(AO_ADC_PIN1_PORT, AO_ADC_PIN1_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN2_PORT
+       stm_moder_set(AO_ADC_PIN2_PORT, AO_ADC_PIN2_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN3_PORT
+       stm_moder_set(AO_ADC_PIN3_PORT, AO_ADC_PIN3_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN4_PORT
+       stm_moder_set(AO_ADC_PIN4_PORT, AO_ADC_PIN4_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN5_PORT
+       stm_moder_set(AO_ADC_PIN5_PORT, AO_ADC_PIN5_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN6_PORT
+       stm_moder_set(AO_ADC_PIN6_PORT, AO_ADC_PIN6_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN7_PORT
+       stm_moder_set(AO_ADC_PIN7_PORT, AO_ADC_PIN7_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN8_PORT
+       stm_moder_set(AO_ADC_PIN8_PORT, AO_ADC_PIN8_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN9_PORT
+       stm_moder_set(AO_ADC_PIN9_PORT, AO_ADC_PIN9_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN10_PORT
+       stm_moder_set(AO_ADC_PIN10_PORT, AO_ADC_PIN10_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN11_PORT
+       stm_moder_set(AO_ADC_PIN11_PORT, AO_ADC_PIN11_PIN, STM_MODER_ANALOG);
+#endif
+#ifdef AO_ADC_PIN12_PORT
+       stm_moder_set(AO_ADC_PIN12_PORT, AO_ADC_PIN12_PIN, STM_MODER_ANALOG);
+#endif
+
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_ADC1EN);
+
+       /* Turn off ADC during configuration */
+       stm_adc.cr2 = 0;
+
+       stm_adc.cr1 = ((0 << STM_ADC_CR1_OVRIE ) |
+                      (STM_ADC_CR1_RES_12 << STM_ADC_CR1_RES ) |
+                      (0 << STM_ADC_CR1_AWDEN ) |
+                      (0 << STM_ADC_CR1_JAWDEN ) |
+                      (0 << STM_ADC_CR1_PDI ) |
+                      (0 << STM_ADC_CR1_PDD ) |
+                      (0 << STM_ADC_CR1_DISCNUM ) |
+                      (0 << STM_ADC_CR1_JDISCEN ) |
+                      (0 << STM_ADC_CR1_DISCEN ) |
+                      (0 << STM_ADC_CR1_JAUTO ) |
+                      (0 << STM_ADC_CR1_AWDSGL ) |
+                      (1 << STM_ADC_CR1_SCAN ) |
+                      (0 << STM_ADC_CR1_JEOCIE ) |
+                      (0 << STM_ADC_CR1_AWDIE ) |
+                      (0 << STM_ADC_CR1_EOCIE ) |
+                      (0 << STM_ADC_CR1_AWDCH ));
+
+       /* 384 cycle sample time for everyone */
+       stm_adc.smpr1 = 0x3ffff;
+       stm_adc.smpr2 = 0x3fffffff;
+       stm_adc.smpr3 = 0x3fffffff;
+
+       stm_adc.sqr1 = ((AO_NUM_ADC - 1) << 20);
+       stm_adc.sqr2 = 0;
+       stm_adc.sqr3 = 0;
+       stm_adc.sqr4 = 0;
+       stm_adc.sqr5 = 0;
+#if AO_NUM_ADC > 0
+       stm_adc.sqr5 |= (AO_ADC_SQ1 << 0);
+#endif
+#if AO_NUM_ADC > 1
+       stm_adc.sqr5 |= (AO_ADC_SQ2 << 5);
+#endif
+#if AO_NUM_ADC > 2
+       stm_adc.sqr5 |= (AO_ADC_SQ3 << 10);
+#endif
+#if AO_NUM_ADC > 3
+       stm_adc.sqr5 |= (AO_ADC_SQ4 << 15);
+#endif
+#if AO_NUM_ADC > 4
+       stm_adc.sqr5 |= (AO_ADC_SQ5 << 20);
+#endif
+#if AO_NUM_ADC > 5
+       stm_adc.sqr5 |= (AO_ADC_SQ6 << 25);
+#endif
+#if AO_NUM_ADC > 6
+       stm_adc.sqr4 |= (AO_ADC_SQ7 << 0);
+#endif
+#if AO_NUM_ADC > 7
+       stm_adc.sqr4 |= (AO_ADC_SQ8 << 5);
+#endif
+#if AO_NUM_ADC > 8
+       stm_adc.sqr4 |= (AO_ADC_SQ9 << 10);
+#endif
+#if AO_NUM_ADC > 9
+       stm_adc.sqr4 |= (AO_ADC_SQ10 << 15);
+#endif
+#if AO_NUM_ADC > 10
+       stm_adc.sqr4 |= (AO_ADC_SQ11 << 20);
+#endif
+#if AO_NUM_ADC > 11
+       stm_adc.sqr4 |= (AO_ADC_SQ12 << 25);
+#endif
+#if AO_NUM_ADC > 12
+#error "need to finish stm_adc.sqr settings"
+#endif
+       
+       /* Turn ADC on */
+       stm_adc.cr2 = AO_ADC_CR2_VAL;
+
+       /* Wait for ADC to be ready */
+       while (!(stm_adc.sr & (1 << STM_ADC_SR_ADONS)))
+               ;
+
+#if HAS_ADC_TEMP
+       stm_adc.ccr = ((1 << STM_ADC_CCR_TSVREFE));
+#else
+       stm_adc.ccr = 0;
+#endif
+       /* Clear any stale status bits */
+       stm_adc.sr = 0;
+
+       ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC1));
+
+       ao_cmd_register(&ao_adc_cmds[0]);
+
+       ao_adc_ready = 1;
+}
diff --git a/src/stm/ao_arch.h b/src/stm/ao_arch.h
new file mode 100644 (file)
index 0000000..87eda18
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ARCH_H_
+#define _AO_ARCH_H_
+
+#include <stdio.h>
+#include <stm32l.h>
+
+/*
+ * STM32L definitions and code fragments for AltOS
+ */
+
+#define AO_STACK_SIZE  512
+
+#define AO_LED_TYPE    uint16_t
+
+#ifndef AO_TICK_TYPE
+#define AO_TICK_TYPE   uint16_t
+#define AO_TICK_SIGNED int16_t
+#endif
+
+/* Various definitions to make GCC look more like SDCC */
+
+#define ao_arch_naked_declare  __attribute__((naked))
+#define ao_arch_naked_define
+#define __pdata
+#define __data
+#define __xdata
+#define __code const
+#define __reentrant
+#define __critical
+#define __interrupt(n)
+#define __at(n)
+
+#define CORTEX_M3_AIRCR                ((uint32_t *) 0xe000ed0c)
+
+#define ao_arch_reboot()       (*((uint32_t *) 0xe000ed0c) = 0x05fa0004)
+
+#define ao_arch_nop()          asm("nop")
+
+#define ao_arch_interrupt(n)   /* nothing */
+
+#undef putchar
+#undef getchar
+#define putchar(c)     ao_putchar(c)
+#define getchar                ao_getchar
+
+extern void putchar(char c);
+extern char getchar(void);
+extern void ao_avr_stdio_init(void);
+
+
+/*
+ * ao_romconfig.c
+ */
+
+#define AO_ROMCONFIG_VERSION   2
+
+#define AO_ROMCONFIG_SYMBOL(a) __attribute__((section(".romconfig"))) const
+
+extern const uint16_t ao_romconfig_version;
+extern const uint16_t ao_romconfig_check;
+extern const uint16_t ao_serial_number;
+extern const uint32_t ao_radio_cal;
+
+#define ARM_PUSH32(stack, val) (*(--(stack)) = (val))
+
+#define ao_arch_task_members\
+       uint32_t *sp;                   /* saved stack pointer */
+
+#define cli()  asm("cpsid i")
+#define sei()  asm("cpsie i")
+
+#define ao_arch_init_stack(task, start) do {                           \
+               uint32_t        *sp = (uint32_t *) (task->stack + AO_STACK_SIZE); \
+               uint32_t        a = (uint32_t) start;                   \
+               int             i;                                      \
+                                                                       \
+               /* Return address (goes into LR) */                     \
+               ARM_PUSH32(sp, a);                                      \
+                                                                       \
+               /* Clear register values r0-r12 */                      \
+               i = 13;                                                 \
+               while (i--)                                             \
+                       ARM_PUSH32(sp, 0);                              \
+                                                                       \
+               /* APSR */                                              \
+               ARM_PUSH32(sp, 0);                                      \
+                                                                       \
+               /* PRIMASK with interrupts enabled */                   \
+               ARM_PUSH32(sp, 0);                                      \
+                                                                       \
+               task->sp = sp;                                          \
+} while (0);
+       
+#define ao_arch_save_regs()    do {                                    \
+               /* Save general registers */                            \
+               asm("push {r0-r12,lr}\n");                              \
+                                                                       \
+               /* Save APSR */                                         \
+               asm("mrs r0,apsr");                                     \
+               asm("push {r0}");                                       \
+                                                                       \
+               /* Save PRIMASK */                                      \
+               asm("mrs r0,primask");                                  \
+               asm("push {r0}");                                       \
+                                                                       \
+               /* Enable interrupts */                                 \
+               sei();                                                  \
+       } while (0)
+
+#define ao_arch_save_stack() do {                                      \
+               uint32_t        *sp;                                    \
+               asm("mov %0,sp" : "=&r" (sp) );                         \
+               ao_cur_task->sp = (sp);                                 \
+               if ((uint8_t *) sp < &ao_cur_task->stack[0])            \
+                       ao_panic (AO_PANIC_STACK);                      \
+       } while (0)
+
+#if 0
+#define ao_arch_isr_stack() do {                               \
+               uint32_t        *sp = (uint32_t *) 0x20004000;  \
+               asm("mov %0,sp" : "=&r" (sp) );                 \
+       } while (0)
+#else
+#define ao_arch_isr_stack()
+#endif
+
+
+#define ao_arch_cpu_idle() do {                        \
+               asm("wfi");             \
+       } while (0)
+
+#define ao_arch_restore_stack() do { \
+               uint32_t        sp;                                     \
+               sp = (uint32_t) ao_cur_task->sp;                        \
+                                                                       \
+               /* Switch stacks */                                     \
+               asm("mov sp, %0" : : "r" (sp) );                        \
+                                                                       \
+               /* Restore PRIMASK */                                   \
+               asm("pop {r0}");                                        \
+               asm("msr primask,r0");                                  \
+                                                                       \
+               /* Restore APSR */                                      \
+               asm("pop {r0}");                                        \
+               asm("msr apsr,r0");                                     \
+                                                                       \
+               /* Restore general registers */                         \
+               asm("pop {r0-r12,lr}\n");                               \
+                                                                       \
+               /* Return to calling function */                        \
+               asm("bx lr");                                           \
+       } while(0)
+
+#define ao_arch_critical(b) do { cli(); do { b } while (0); sei(); } while (0)
+
+/*
+ * For now, we're running at a weird frequency
+ */
+
+#if AO_HSE
+#define AO_PLLSRC      AO_HSE
+#else
+#define AO_PLLSRC      STM_HSI_FREQ
+#endif
+
+#define AO_PLLVCO      (AO_PLLSRC * AO_PLLMUL)
+#define AO_SYSCLK      (AO_PLLVCO / AO_PLLDIV)
+#define AO_HCLK                (AO_SYSCLK / AO_AHB_PRESCALER)
+#define AO_PCLK1       (AO_HCLK / AO_APB1_PRESCALER)
+#define AO_PCLK2       (AO_HCLK / AO_APB2_PRESCALER)
+
+#if AO_APB1_PRESCALER == 1
+#define AO_TIM23467_CLK                AO_PCLK1
+#else
+#define AO_TIM23467_CLK                (2 * AO_PCLK1)
+#endif
+
+#if AO_APB2_PRESCALER == 1
+#define AO_TIM91011_CLK                AO_PCLK2
+#else
+#define AO_TIM91011_CLK                (2 * AO_PCLK2)
+#endif
+
+#define AO_STM_NVIC_HIGH_PRIORITY      4
+#define AO_STM_NVIC_CLOCK_PRIORITY     6
+#define AO_STM_NVIC_MED_PRIORITY       8
+#define AO_STM_NVIC_LOW_PRIORITY       10
+
+void ao_lcd_stm_init(void);
+
+void ao_lcd_font_init(void);
+
+void ao_lcd_font_string(char *s);
+
+char
+ao_serial1_getchar(void);
+
+void
+ao_serial1_putchar(char c);
+
+char
+ao_serial1_pollchar(void);
+
+void
+ao_serial1_set_speed(uint8_t speed);
+
+char
+ao_serial2_getchar(void);
+
+void
+ao_serial2_putchar(char c);
+
+char
+ao_serial2_pollchar(void);
+
+void
+ao_serial2_set_speed(uint8_t speed);
+
+char
+ao_serial3_getchar(void);
+
+void
+ao_serial3_putchar(char c);
+
+char
+ao_serial3_pollchar(void);
+
+void
+ao_serial3_set_speed(uint8_t speed);
+
+extern const uint32_t  ao_radio_cal;
+
+void
+ao_adc_init();
+
+#endif /* _AO_ARCH_H_ */
+
diff --git a/src/stm/ao_arch_funcs.h b/src/stm/ao_arch_funcs.h
new file mode 100644 (file)
index 0000000..d4fbea3
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_ARCH_FUNCS_H_
+#define _AO_ARCH_FUNCS_H_
+
+/* ao_spi_stm.c
+ */
+
+#define AO_SPI_SPEED_FAST      STM_SPI_CR1_BR_PCLK_4
+#define AO_SPI_SPEED_1MHz      STM_SPI_CR1_BR_PCLK_16
+#define AO_SPI_SPEED_200kHz    STM_SPI_CR1_BR_PCLK_256
+
+#define AO_SPI_CONFIG_1                0x00
+#define AO_SPI_1_CONFIG_PA5_PA6_PA7    AO_SPI_CONFIG_1
+#define AO_SPI_2_CONFIG_PB13_PB14_PB15 AO_SPI_CONFIG_1
+
+#define AO_SPI_CONFIG_2                0x04
+#define AO_SPI_1_CONFIG_PB3_PB4_PB5    AO_SPI_CONFIG_2
+#define AO_SPI_2_CONFIG_PD1_PD3_PD4    AO_SPI_CONFIG_2
+
+#define AO_SPI_CONFIG_3                0x08
+#define AO_SPI_1_CONFIG_PE13_PE14_PE15 AO_SPI_CONFIG_3
+
+#define AO_SPI_CONFIG_NONE     0x0c
+
+#define AO_SPI_INDEX_MASK      0x01
+#define AO_SPI_CONFIG_MASK     0x0c
+
+#define AO_SPI_1_PA5_PA6_PA7   (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PA5_PA6_PA7)
+#define AO_SPI_1_PB3_PB4_PB5   (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PB3_PB4_PB5)
+#define AO_SPI_1_PE13_PE14_PE15        (STM_SPI_INDEX(1) | AO_SPI_1_CONFIG_PE13_PE14_PE15)
+
+#define AO_SPI_2_PB13_PB14_PB15        (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PB13_PB14_PB15)
+#define AO_SPI_2_PD1_PD3_PD4   (STM_SPI_INDEX(2) | AO_SPI_2_CONFIG_PD1_PD3_PD4)
+
+#define AO_SPI_INDEX(id)       ((id) & AO_SPI_INDEX_MASK)
+#define AO_SPI_CONFIG(id)      ((id) & AO_SPI_CONFIG_MASK)
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed);
+
+void
+ao_spi_put(uint8_t spi_index);
+
+void
+ao_spi_send(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index);
+
+void
+ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index);
+
+extern uint16_t        ao_spi_speed[STM_NUM_SPI];
+
+void
+ao_spi_init(void);
+
+#define ao_spi_get_mask(reg,mask,bus, speed) do {              \
+               ao_spi_get(bus, speed);                         \
+               (reg)->bsrr = ((uint32_t) mask) << 16;  \
+       } while (0)
+
+#define ao_spi_put_mask(reg,mask,bus) do {     \
+               (reg)->bsrr = mask;             \
+               ao_spi_put(bus);                \
+       } while (0)
+
+#define ao_spi_get_bit(reg,bit,pin,bus,speed) ao_spi_get_mask(reg,(1<<bit),bus,speed)
+#define ao_spi_put_bit(reg,bit,pin,bus) ao_spi_put_mask(reg,(1<<bit),bus)
+
+#define ao_enable_port(port) do {                                      \
+               if ((port) == &stm_gpioa)                               \
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN); \
+               else if ((port) == &stm_gpiob)                          \
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN); \
+               else if ((port) == &stm_gpioc)                          \
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN); \
+               else if ((port) == &stm_gpiod)                          \
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN); \
+               else if ((port) == &stm_gpioe)                          \
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN); \
+       } while (0)
+
+
+#define ao_gpio_set(port, bit, pin, v) stm_gpio_set(port, bit, v)
+
+#define ao_gpio_get(port, bit, pin) stm_gpio_get(port, bit)
+
+#define ao_enable_output(port,bit,pin,v) do {                  \
+               ao_enable_port(port);                           \
+               ao_gpio_set(port, bit, pin, v);                 \
+               stm_moder_set(port, bit, STM_MODER_OUTPUT);\
+       } while (0)
+
+#define ao_enable_input(port,bit,mode) do {                            \
+               ao_enable_port(port);                                   \
+               stm_moder_set(port, bit, STM_MODER_INPUT);              \
+               if (mode == AO_EXTI_MODE_PULL_UP)                       \
+                       stm_pupdr_set(port, bit, STM_PUPDR_PULL_UP);    \
+               else if (mode == AO_EXTI_MODE_PULL_DOWN)                \
+                       stm_pupdr_set(port, bit, STM_PUPDR_PULL_DOWN);  \
+               else                                                    \
+                       stm_pupdr_set(port, bit, STM_PUPDR_NONE);       \
+       } while (0)
+
+#define ao_enable_cs(port,bit) do {                            \
+               stm_gpio_set((port), bit, 1);                   \
+               stm_moder_set((port), bit, STM_MODER_OUTPUT);   \
+       } while (0)
+
+#define ao_spi_init_cs(port, mask) do {                                \
+               ao_enable_port(port);                           \
+               if ((mask) & 0x0001) ao_enable_cs(port, 0);     \
+               if ((mask) & 0x0002) ao_enable_cs(port, 1);     \
+               if ((mask) & 0x0004) ao_enable_cs(port, 2);     \
+               if ((mask) & 0x0008) ao_enable_cs(port, 3);     \
+               if ((mask) & 0x0010) ao_enable_cs(port, 4);     \
+               if ((mask) & 0x0020) ao_enable_cs(port, 5);     \
+               if ((mask) & 0x0040) ao_enable_cs(port, 6);     \
+               if ((mask) & 0x0080) ao_enable_cs(port, 7);     \
+               if ((mask) & 0x0100) ao_enable_cs(port, 8);     \
+               if ((mask) & 0x0200) ao_enable_cs(port, 9);     \
+               if ((mask) & 0x0400) ao_enable_cs(port, 10);\
+               if ((mask) & 0x0800) ao_enable_cs(port, 11);\
+               if ((mask) & 0x1000) ao_enable_cs(port, 12);\
+               if ((mask) & 0x2000) ao_enable_cs(port, 13);\
+               if ((mask) & 0x4000) ao_enable_cs(port, 14);\
+               if ((mask) & 0x8000) ao_enable_cs(port, 15);\
+       } while (0)
+
+/* ao_dma_stm.c
+ */
+
+extern uint8_t ao_dma_done[STM_NUM_DMA];
+
+void
+ao_dma_set_transfer(uint8_t            index,
+                   volatile void       *peripheral,
+                   void                *memory,
+                   uint16_t            count,
+                   uint32_t            ccr);
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int index));
+
+void
+ao_dma_start(uint8_t index);
+
+void
+ao_dma_done_transfer(uint8_t index);
+
+void
+ao_dma_abort(uint8_t index);
+
+void
+ao_dma_alloc(uint8_t index);
+
+void
+ao_dma_init(void);
+
+/* ao_i2c_stm.c */
+
+void
+ao_i2c_get(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_start(uint8_t i2c_index, uint16_t address);
+
+void
+ao_i2c_put(uint8_t i2c_index);
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t i2c_index, uint8_t stop);
+
+void
+ao_i2c_init(void);
+
+#endif /* _AO_ARCH_FUNCS_H_ */
diff --git a/src/stm/ao_beep_stm.c b/src/stm/ao_beep_stm.c
new file mode 100644 (file)
index 0000000..37c30e2
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+void
+ao_beep(uint8_t beep)
+{
+       if (beep == 0) {
+               stm_tim3.cr1 = 0;
+               stm_rcc.apb1enr &= ~(1 << STM_RCC_APB1ENR_TIM3EN);
+       } else {
+               stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM3EN);
+
+               stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                               (0 << STM_TIM234_CR1_ARPE) |
+                               (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                               (0 << STM_TIM234_CR1_DIR) |
+                               (0 << STM_TIM234_CR1_OPM) |
+                               (0 << STM_TIM234_CR1_URS) |
+                               (0 << STM_TIM234_CR1_UDIS) |
+                               (0 << STM_TIM234_CR1_CEN));
+
+               stm_tim3.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+                               (STM_TIM234_CR2_MMS_RESET << STM_TIM234_CR2_MMS) |
+                               (0 << STM_TIM234_CR2_CCDS));
+
+               /* Set prescaler to match cc1111 clocks
+                */
+               stm_tim3.psc = AO_TIM23467_CLK / 750000;
+
+               /* 1. Select the counter clock (internal, external, prescaler).
+                *
+                * Setting SMCR to zero means use the internal clock
+                */
+
+               stm_tim3.smcr = 0;
+
+               /* 2. Write the desired data in the TIMx_ARR and TIMx_CCRx registers. */
+               stm_tim3.arr = beep;
+               stm_tim3.ccr1 = beep;
+
+               /* 3. Set the CCxIE and/or CCxDE bits if an interrupt and/or a
+                * DMA request is to be generated.
+                */
+               /* don't want this */
+
+               /* 4. Select the output mode. For example, you must write
+                *  OCxM=011, OCxPE=0, CCxP=0 and CCxE=1 to toggle OCx output
+                *  pin when CNT matches CCRx, CCRx preload is not used, OCx
+                *  is enabled and active high.
+                */
+
+               stm_tim3.ccmr1 = ((0 << STM_TIM234_CCMR1_OC2CE) |
+                                 (STM_TIM234_CCMR1_OC2M_FROZEN << STM_TIM234_CCMR1_OC2M) |
+                                 (0 << STM_TIM234_CCMR1_OC2PE) |
+                                 (0 << STM_TIM234_CCMR1_OC2FE) |
+                                 (STM_TIM234_CCMR1_CC2S_OUTPUT << STM_TIM234_CCMR1_CC2S) |
+
+                                 (0 << STM_TIM234_CCMR1_OC1CE) |
+                                 (STM_TIM234_CCMR1_OC1M_TOGGLE << STM_TIM234_CCMR1_OC1M) |
+                                 (0 << STM_TIM234_CCMR1_OC1PE) |
+                                 (0 << STM_TIM234_CCMR1_OC1FE) |
+                                 (STM_TIM234_CCMR1_CC1S_OUTPUT << STM_TIM234_CCMR1_CC1S));
+
+
+               stm_tim3.ccer = ((0 << STM_TIM234_CCER_CC4NP) |
+                                (0 << STM_TIM234_CCER_CC4P) |
+                                (0 << STM_TIM234_CCER_CC4E) |
+                                (0 << STM_TIM234_CCER_CC3NP) |
+                                (0 << STM_TIM234_CCER_CC3P) |
+                                (0 << STM_TIM234_CCER_CC3E) |
+                                (0 << STM_TIM234_CCER_CC2NP) |
+                                (0 << STM_TIM234_CCER_CC2P) |
+                                (0 << STM_TIM234_CCER_CC2E) |
+                                (0 << STM_TIM234_CCER_CC1NP) |
+                                (0 << STM_TIM234_CCER_CC1P) |
+                                (1 << STM_TIM234_CCER_CC1E));
+
+
+               /* 5. Enable the counter by setting the CEN bit in the TIMx_CR1 register. */
+
+               stm_tim3.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                               (0 << STM_TIM234_CR1_ARPE) |
+                               (STM_TIM234_CR1_CMS_EDGE << STM_TIM234_CR1_CMS) |
+                               (0 << STM_TIM234_CR1_DIR) |
+                               (0 << STM_TIM234_CR1_OPM) |
+                               (0 << STM_TIM234_CR1_URS) |
+                               (0 << STM_TIM234_CR1_UDIS) |
+                               (1 << STM_TIM234_CR1_CEN));
+       }
+}
+
+void
+ao_beep_for(uint8_t beep, uint16_t ticks) __reentrant
+{
+       ao_beep(beep);
+       ao_delay(ticks);
+       ao_beep(0);
+}
+
+void
+ao_beep_init(void)
+{
+       /* Our beeper is on PC6, which is hooked to TIM3_CH1,
+        * which is on PC6
+        */
+
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN);
+
+       stm_afr_set(&stm_gpioc, 6, STM_AFR_AF2);
+
+       /* Leave the timer off until requested */
+       
+       stm_rcc.apb1enr &= ~(1 << STM_RCC_APB1ENR_TIM3EN);
+}
diff --git a/src/stm/ao_data.c b/src/stm/ao_data.c
new file mode 100644 (file)
index 0000000..38d2f7f
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_data.h>
+
+volatile __xdata struct ao_data        ao_data_ring[AO_DATA_RING];
+volatile __data uint8_t                ao_data_head;
+volatile __data uint8_t                ao_data_present;
+
+void
+ao_data_get(__xdata struct ao_data *packet)
+{
+#if HAS_FLIGHT
+       uint8_t i = ao_data_ring_prev(ao_sample_data);
+#else
+       uint8_t i = ao_data_ring_prev(ao_data_head);
+#endif
+       memcpy(packet, (void *) &ao_data_ring[i], sizeof (struct ao_data));
+}
diff --git a/src/stm/ao_dma_stm.c b/src/stm/ao_dma_stm.c
new file mode 100644 (file)
index 0000000..8379a1a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#define NUM_DMA        7
+
+struct ao_dma_config {
+       void            (*isr)(int index);
+};
+
+uint8_t ao_dma_done[NUM_DMA];
+
+static struct ao_dma_config ao_dma_config[NUM_DMA];
+static uint8_t ao_dma_allocated[NUM_DMA];
+static uint8_t ao_dma_mutex[NUM_DMA];
+static uint8_t ao_dma_active;
+
+static void
+ao_dma_isr(uint8_t index) {
+       /* Get channel interrupt bits */
+       uint32_t        isr = stm_dma.isr & (STM_DMA_ISR_MASK <<
+                                            STM_DMA_ISR(index));
+
+       /* Ack them */
+       stm_dma.ifcr = isr;
+       if (ao_dma_config[index].isr)
+               (*ao_dma_config[index].isr)(index);
+       else {
+               ao_dma_done[index] = 1;
+               ao_wakeup(&ao_dma_done[index]);
+       }
+}
+
+void stm_dma1_channel1_isr(void) { ao_dma_isr(STM_DMA_INDEX(1)); }
+void stm_dma1_channel2_isr(void) { ao_dma_isr(STM_DMA_INDEX(2)); }
+void stm_dma1_channel3_isr(void) { ao_dma_isr(STM_DMA_INDEX(3)); }
+void stm_dma1_channel4_isr(void) { ao_dma_isr(STM_DMA_INDEX(4)); }
+void stm_dma1_channel5_isr(void) { ao_dma_isr(STM_DMA_INDEX(5)); }
+void stm_dma1_channel6_isr(void) { ao_dma_isr(STM_DMA_INDEX(6)); }
+void stm_dma1_channel7_isr(void) { ao_dma_isr(STM_DMA_INDEX(7)); }
+
+void
+ao_dma_set_transfer(uint8_t            index,
+                   volatile void       *peripheral,
+                   void                *memory,
+                   uint16_t            count,
+                   uint32_t            ccr)
+{
+       if (ao_dma_allocated[index]) {
+               if (ao_dma_mutex[index])
+                       ao_panic(AO_PANIC_DMA);
+               ao_dma_mutex[index] = 1;
+       } else
+               ao_mutex_get(&ao_dma_mutex[index]);
+       ao_arch_critical(
+               if (ao_dma_active++ == 0)
+                       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_DMA1EN);
+               );
+       stm_dma.channel[index].ccr = ccr | (1 << STM_DMA_CCR_TCIE);
+       stm_dma.channel[index].cndtr = count;
+       stm_dma.channel[index].cpar = peripheral;
+       stm_dma.channel[index].cmar = memory;
+       ao_dma_config[index].isr = NULL;
+}
+
+void
+ao_dma_set_isr(uint8_t index, void (*isr)(int))
+{
+       ao_dma_config[index].isr = isr;
+}
+
+void
+ao_dma_start(uint8_t index)
+{
+       ao_dma_done[index] = 0;
+       stm_dma.channel[index].ccr |= (1 << STM_DMA_CCR_EN);
+}
+
+void
+ao_dma_done_transfer(uint8_t index)
+{
+       stm_dma.channel[index].ccr &= ~(1 << STM_DMA_CCR_EN);
+       ao_arch_critical(
+               if (--ao_dma_active == 0)
+                       stm_rcc.ahbenr &= ~(1 << STM_RCC_AHBENR_DMA1EN);
+               );
+       if (ao_dma_allocated[index])
+               ao_dma_mutex[index] = 0;
+       else
+               ao_mutex_put(&ao_dma_mutex[index]);
+}
+
+void
+ao_dma_abort(uint8_t index)
+{
+       stm_dma.channel[index].ccr &= ~(1 << STM_DMA_CCR_EN);
+       ao_wakeup(&ao_dma_done[index]);
+}
+
+void
+ao_dma_alloc(uint8_t index)
+{
+       if (ao_dma_allocated[index])
+               ao_panic(AO_PANIC_DMA);
+       ao_dma_allocated[index] = 1;
+}
+
+void
+ao_dma_init(void)
+{
+       int     index;
+
+       for (index = 0; index < STM_NUM_DMA; index++) {
+               stm_nvic_set_enable(STM_ISR_DMA1_CHANNEL1_POS + index);
+               stm_nvic_set_priority(STM_ISR_DMA1_CHANNEL1_POS + index, 4);
+               ao_dma_allocated[index] = 0;
+               ao_dma_mutex[index] = 0;
+       }
+       
+}
diff --git a/src/stm/ao_eeprom_stm.c b/src/stm/ao_eeprom_stm.c
new file mode 100644 (file)
index 0000000..5a75a97
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_storage.h>
+
+/* Total bytes of available storage */
+ao_pos_t       ao_storage_total = 4096;
+
+/* Block size - device is erased in these units. */
+ao_pos_t       ao_storage_block = 1024;
+
+/* Byte offset of config block. Will be ao_storage_block bytes long */
+ao_pos_t       ao_storage_config = 0;
+
+/* Storage unit size - device reads and writes must be within blocks of this size. */
+uint16_t       ao_storage_unit = 1024;
+
+/* Location of eeprom in address space */
+#define stm_eeprom     ((uint8_t *) 0x08080000)
+
+/*
+ * The internal flash chip is arranged in 8 byte sectors; the
+ * chip cannot erase in units smaller than that.
+ *
+ * Writing happens in units of 2 bytes and
+ * can only change bits from 1 to 0. So, you can rewrite
+ * the same contents, or append to an existing page easily enough
+ */
+
+/*
+ * Erase the specified sector
+ */
+uint8_t
+ao_storage_erase(ao_pos_t pos) __reentrant
+{
+       /* Not necessary */
+       return 1;
+}
+
+static void
+ao_intflash_unlock(void)
+{
+       /* Unlock Data EEPROM and FLASH_PECR register */
+       stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY1;
+       stm_flash.pekeyr = STM_FLASH_PEKEYR_PEKEY2;
+
+       /* Configure the FTDW bit (FLASH_PECR[8]) to execute
+        * word write, whatever the previous value of the word
+        * being written to
+        */
+       stm_flash.pecr = ((0 << STM_FLASH_PECR_OBL_LAUNCH) |
+                         (0 << STM_FLASH_PECR_ERRIE) |
+                         (0 << STM_FLASH_PECR_EOPIE) |
+                         (0 << STM_FLASH_PECR_FPRG) |
+                         (0 << STM_FLASH_PECR_ERASE) |
+                         (0 << STM_FLASH_PECR_FTDW) |
+                         (1 << STM_FLASH_PECR_DATA) |
+                         (0 << STM_FLASH_PECR_PROG) |
+                         (0 << STM_FLASH_PECR_OPTLOCK) |
+                         (0 << STM_FLASH_PECR_PRGLOCK) |
+                         (0 << STM_FLASH_PECR_PELOCK));
+}
+
+static void
+ao_intflash_lock(void)
+{
+       stm_flash.pecr |= (1 << STM_FLASH_PECR_PELOCK);
+}
+
+static void
+ao_intflash_wait(void)
+{
+       /* Wait for the flash unit to go idle */
+       while (stm_flash.sr & (1 << STM_FLASH_SR_BSY))
+               ;
+}
+
+static void
+ao_intflash_write32(uint16_t pos, uint32_t w)
+{
+       volatile uint32_t       *addr;
+
+       addr = (uint32_t *) (stm_eeprom + pos);
+
+       /* Erase previous word */
+       *addr = 0;
+       ao_intflash_wait();
+
+       if (w) {
+               /* Write a word to a valid address in the data EEPROM */
+               *addr = w;
+               ao_intflash_wait();
+       }
+}
+
+static void
+ao_intflash_write8(uint16_t pos, uint8_t d)
+{
+       uint32_t        w, *addr, mask;
+       uint8_t         shift;
+       
+       addr = (uint32_t *) (stm_eeprom + (pos & ~3));
+
+       /* Compute word to be written */
+       shift = (pos & 3) << 3;
+       mask = 0xff << shift;
+       w = (*addr & ~mask) | (d << shift);
+
+       ao_intflash_write32(pos & ~3, w);
+}
+
+static uint8_t
+ao_intflash_read(uint16_t pos)
+{
+       return stm_eeprom[pos];
+}
+
+/*
+ * Write to flash
+ */
+
+uint8_t
+ao_storage_device_write(ao_pos_t pos32, __xdata void *v, uint16_t len) __reentrant
+{
+       uint16_t pos = pos32;
+       __xdata uint8_t *d = v;
+
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+
+       ao_intflash_unlock();
+       while (len) {
+               if ((pos & 3) == 0 && len >= 4) {
+                       uint32_t        w;
+
+                       w = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
+                       ao_intflash_write32(pos, w);
+                       pos += 4;
+                       d += 4;
+                       len -= 4;
+               } else {
+                       ao_intflash_write8(pos, *d);
+                       pos += 1;
+                       d += 1;
+                       len -= 1;
+               }
+       }
+       ao_intflash_lock();
+
+       return 1;
+}
+
+/*
+ * Read from flash
+ */
+uint8_t
+ao_storage_device_read(ao_pos_t pos, __xdata void *v, uint16_t len) __reentrant
+{
+       uint8_t *d = v;
+       
+       if (pos >= ao_storage_total || pos + len > ao_storage_total)
+               return 0;
+       while (len--)
+               *d++ = ao_intflash_read(pos++);
+       return 1;
+}
+
+void
+ao_storage_flush(void) __reentrant
+{
+}
+
+void
+ao_storage_setup(void)
+{
+}
+
+void
+ao_storage_device_info(void) __reentrant
+{
+       uint8_t i;
+       printf ("Using internal flash\n");
+}
+
+void
+ao_storage_device_init(void)
+{
+}
diff --git a/src/stm/ao_exti.h b/src/stm/ao_exti.h
new file mode 100644 (file)
index 0000000..35b56b5
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_EXTI_H_
+#define _AO_EXTI_H_
+
+#define AO_EXTI_MODE_RISING    1
+#define AO_EXTI_MODE_FALLING   2
+#define AO_EXTI_MODE_PULL_UP   4
+#define AO_EXTI_MODE_PULL_DOWN 8
+#define AO_EXTI_PRIORITY_LOW   16
+#define AO_EXTI_PRIORITY_MED   0
+#define AO_EXTI_PRIORITY_HIGH  32
+
+void
+ao_exti_setup(struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)());
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode);
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)());
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin);
+
+void
+ao_exti_init(void);
+
+#endif /* _AO_EXTI_H_ */
diff --git a/src/stm/ao_exti_stm.c b/src/stm/ao_exti_stm.c
new file mode 100644 (file)
index 0000000..1361d0d
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+
+static void    (*ao_exti_callback[16])(void);
+
+uint32_t       ao_last_exti;
+
+static void ao_exti_one_isr(uint8_t pin) {
+       uint32_t        pending = (ao_last_exti = stm_exti.pr) & (1 << pin);
+
+       stm_exti.pr = pending;
+       if (pending && ao_exti_callback[pin])
+               (*ao_exti_callback[pin])();
+}
+
+static void ao_exti_range_isr(uint8_t first, uint8_t last, uint16_t mask) {
+       uint16_t        pending = (ao_last_exti = stm_exti.pr) & mask;
+       uint8_t         pin;
+       static uint16_t last_mask;
+       static uint8_t  last_pin;
+
+       if (pending == last_mask) {
+               stm_exti.pr = last_mask;
+               (*ao_exti_callback[last_pin])();
+               return;
+       }
+       stm_exti.pr = pending;
+       for (pin = first; pin <= last; pin++)
+               if ((pending & ((uint32_t) 1 << pin)) && ao_exti_callback[pin]) {
+                       last_mask = (1 << pin);
+                       last_pin = pin;
+                       (*ao_exti_callback[pin])();
+               }
+}
+
+void stm_exti0_isr(void) { ao_exti_one_isr(0); }
+void stm_exti1_isr(void) { ao_exti_one_isr(1); }
+void stm_exti2_isr(void) { ao_exti_one_isr(2); }
+void stm_exti3_isr(void) { ao_exti_one_isr(3); }
+void stm_exti4_isr(void) { ao_exti_one_isr(4); }
+void stm_exti9_5_isr(void) { ao_exti_range_isr(5, 9, 0x3e0); }
+void stm_exti15_10_isr(void) { ao_exti_range_isr(10, 15, 0xfc00); }
+
+void
+ao_exti_setup (struct stm_gpio *gpio, uint8_t pin, uint8_t mode, void (*callback)(void)) {
+       uint32_t        mask = 1 << pin;
+       uint32_t        pupdr;
+       uint8_t         irq;
+       uint8_t         prio;
+
+       ao_exti_callback[pin] = callback;
+
+       /* configure gpio to interrupt routing */
+       stm_exticr_set(gpio, pin);
+
+       /* configure pin as input, setting selected pull-up/down mode */
+       stm_moder_set(gpio, pin, STM_MODER_INPUT);
+       switch (mode & (AO_EXTI_MODE_PULL_UP|AO_EXTI_MODE_PULL_DOWN)) {
+       case 0:
+       default:
+               pupdr  = STM_PUPDR_NONE;
+               break;
+       case AO_EXTI_MODE_PULL_UP:
+               pupdr = STM_PUPDR_PULL_UP;
+               break;
+       case AO_EXTI_MODE_PULL_DOWN:
+               pupdr = STM_PUPDR_PULL_DOWN;
+               break;
+       }
+       stm_pupdr_set(gpio, pin, pupdr);
+
+       /* Set interrupt mask and rising/falling mode */
+       stm_exti.imr &= ~mask;
+       if (mode & AO_EXTI_MODE_RISING)
+               stm_exti.rtsr |= mask;
+       else
+               stm_exti.rtsr &= ~mask;
+       if (mode & AO_EXTI_MODE_FALLING)
+               stm_exti.ftsr |= mask;
+       else
+               stm_exti.ftsr &= ~mask;
+
+       if (pin <= 4)
+               irq = STM_ISR_EXTI0_POS + pin;
+       else if (pin <= 9)
+               irq = STM_ISR_EXTI9_5_POS;
+       else
+               irq = STM_ISR_EXTI15_10_POS;
+
+       /* Set priority */
+       prio = AO_STM_NVIC_MED_PRIORITY;
+       if (mode & AO_EXTI_PRIORITY_LOW)
+               prio = AO_STM_NVIC_LOW_PRIORITY;
+       else if (mode & AO_EXTI_PRIORITY_HIGH)
+               prio = AO_STM_NVIC_HIGH_PRIORITY;
+
+       stm_nvic_set_priority(irq, prio);
+       stm_nvic_set_enable(irq);
+}
+
+void
+ao_exti_set_mode(struct stm_gpio *gpio, uint8_t pin, uint8_t mode) {
+       uint32_t        mask = 1 << pin;
+       
+       if (mode & AO_EXTI_MODE_RISING)
+               stm_exti.rtsr |= mask;
+       else
+               stm_exti.rtsr &= ~mask;
+       if (mode & AO_EXTI_MODE_FALLING)
+               stm_exti.ftsr |= mask;
+       else
+               stm_exti.ftsr &= ~mask;
+}
+
+void
+ao_exti_set_callback(struct stm_gpio *gpio, uint8_t pin, void (*callback)()) {
+       ao_exti_callback[pin] = callback;
+}
+
+void
+ao_exti_enable(struct stm_gpio *gpio, uint8_t pin) {
+       uint32_t        mask = (1 << pin);
+       stm_exti.pr = mask;
+       stm_exti.imr |= (1 << pin);
+}
+
+void
+ao_exti_disable(struct stm_gpio *gpio, uint8_t pin) {
+       uint32_t        mask = (1 << pin);
+       stm_exti.imr &= ~mask;
+       stm_exti.pr = mask;
+}
+
+void
+ao_exti_init(void)
+{
+}
diff --git a/src/stm/ao_i2c_stm.c b/src/stm/ao_i2c_stm.c
new file mode 100644 (file)
index 0000000..b6dd705
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+struct ao_i2c_stm_info {
+       uint8_t tx_dma_index;
+       uint8_t rx_dma_index;
+       struct stm_i2c  *stm_i2c;
+};
+
+#define I2C_FAST       1
+
+#define I2C_TIMEOUT    100
+
+#define I2C_IDLE       0
+#define I2C_RUNNING    1
+#define I2C_ERROR      2
+
+static uint8_t ao_i2c_state[STM_NUM_I2C];
+static uint16_t        ao_i2c_addr[STM_NUM_I2C];
+uint8_t        ao_i2c_mutex[STM_NUM_I2C];
+
+# define I2C_HIGH_SLOW 5000    /* ns, 100kHz clock */
+#ifdef MEGAMETRUM
+# define I2C_HIGH_FAST 2000    /* ns, 167kHz clock */
+#else
+# define I2C_HIGH_FAST 1000    /* ns, 333kHz clock */
+#endif
+
+# define I2C_RISE_SLOW 500     /* ns */
+# define I2C_RISE_FAST 100     /* ns */
+
+/* Clock period in ns */
+#define CYCLES(period) (((period) * (AO_PCLK1 / 1000)) / 1000000)
+
+#define max(a,b)       ((a) > (b) ? (a) : (b))
+#define I2C_CCR_HIGH_SLOW      max(4,CYCLES(I2C_HIGH_SLOW))
+#define I2C_CCR_HIGH_FAST      max(4,CYCLES(I2C_HIGH_FAST))
+#define I2C_TRISE_SLOW         (CYCLES(I2C_RISE_SLOW) + 1)
+#define I2C_TRISE_FAST         (CYCLES(I2C_RISE_FAST) + 1)
+
+#if I2C_FAST
+#define I2C_TRISE      I2C_TRISE_FAST
+#define I2C_CCR_HIGH   I2C_CCR_HIGH_FAST
+#else
+#define I2C_TRISE      I2C_TRISE_SLOW
+#define I2C_CCR_HIGH   I2C_CCR_HIGH_SLOW
+#endif
+
+#if AO_PCLK1 == 2000000
+# define AO_STM_I2C_CR2_FREQ   STM_I2C_CR2_FREQ_2_MHZ
+#endif
+#if AO_PCLK1 == 4000000
+#  define AO_STM_I2C_CR2_FREQ  STM_I2C_CR2_FREQ_4_MHZ
+#endif
+#if AO_PCLK1 == 8000000
+# define AO_STM_I2C_CR2_FREQ   STM_I2C_CR2_FREQ_8_MHZ
+#endif
+#if AO_PCLK1 == 16000000
+# define AO_STM_I2C_CR2_FREQ   STM_I2C_CR2_FREQ_16_MHZ
+#endif
+#if AO_PCLK1 == 32000000
+# define AO_STM_I2C_CR2_FREQ   STM_I2C_CR2_FREQ_32_MHZ
+#endif
+
+#define AO_STM_I2C_CR1 ((0 << STM_I2C_CR1_SWRST) |     \
+                       (0 << STM_I2C_CR1_ALERT) |      \
+                       (0 << STM_I2C_CR1_PEC) |        \
+                       (0 << STM_I2C_CR1_POS) |        \
+                       (0 << STM_I2C_CR1_ACK) |        \
+                       (0 << STM_I2C_CR1_STOP) |       \
+                       (0 << STM_I2C_CR1_START) |      \
+                       (0 << STM_I2C_CR1_NOSTRETCH) |  \
+                       (0 << STM_I2C_CR1_ENGC) |       \
+                       (0 << STM_I2C_CR1_ENPEC) |      \
+                       (0 << STM_I2C_CR1_ENARP) |      \
+                       (0 << STM_I2C_CR1_SMBTYPE) |    \
+                       (0 << STM_I2C_CR1_SMBUS) |      \
+                       (1 << STM_I2C_CR1_PE))
+
+#define AO_STM_I2C_CR2  ((0 << STM_I2C_CR2_LAST) |                     \
+                        (0 << STM_I2C_CR2_DMAEN) |                     \
+                        (0 << STM_I2C_CR2_ITBUFEN) |                   \
+                        (0 << STM_I2C_CR2_ITEVTEN) |                   \
+                        (0 << STM_I2C_CR2_ITERREN) |                   \
+                        (AO_STM_I2C_CR2_FREQ << STM_I2C_CR2_FREQ))
+
+static const struct ao_i2c_stm_info    ao_i2c_stm_info[STM_NUM_I2C] = {
+       {
+               .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_TX),
+               .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C1_RX),
+               .stm_i2c = &stm_i2c1
+       },
+       {
+               .tx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_TX),
+               .rx_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_I2C2_RX),
+               .stm_i2c = &stm_i2c2
+       },
+};
+
+static uint8_t *ao_i2c_recv_data[STM_NUM_I2C];
+static uint16_t        ao_i2c_recv_len[STM_NUM_I2C];
+static uint16_t        ev_count;
+
+static void
+ao_i2c_ev_isr(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint32_t        sr1;
+
+       ++ev_count;
+       sr1 = stm_i2c->sr1;
+       if (sr1 & (1 << STM_I2C_SR1_SB))
+               stm_i2c->dr = ao_i2c_addr[index];
+       if (sr1 & (1 << STM_I2C_SR1_ADDR)) {
+               stm_i2c->cr2 &= ~(1 << STM_I2C_CR2_ITEVTEN);
+               ao_i2c_state[index] = I2C_RUNNING;
+               ao_wakeup(&ao_i2c_state[index]);
+       }
+       if (sr1 & (1 << STM_I2C_SR1_BTF)) {
+               stm_i2c->cr2 &= ~(1 << STM_I2C_CR2_ITEVTEN);
+               ao_wakeup(&ao_i2c_state[index]);
+       }
+       if (sr1 & (1 << STM_I2C_SR1_RXNE)) {
+               if (ao_i2c_recv_len[index]) {                   
+                       *(ao_i2c_recv_data[index]++) = stm_i2c->dr;
+                       if (!--ao_i2c_recv_len[index])
+                               ao_wakeup(&ao_i2c_recv_len[index]);
+               }
+       }
+}
+
+void stm_i2c1_ev_isr(void) { ao_i2c_ev_isr(0); }
+void stm_i2c2_ev_isr(void) { ao_i2c_ev_isr(1); }
+
+static void
+ao_i2c_er_isr(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint32_t        sr1;
+
+       sr1 = stm_i2c->sr1;
+       if (sr1 & (1 << STM_I2C_SR1_AF)) {
+               ao_i2c_state[index] = I2C_ERROR;
+               stm_i2c->sr1 = sr1 & ~(1 << STM_I2C_SR1_AF);
+               ao_wakeup(&ao_i2c_state[index]);
+       }
+}
+
+void stm_i2c1_er_isr(void) { ao_i2c_er_isr(0); }
+void stm_i2c2_er_isr(void) { ao_i2c_er_isr(1); }
+
+void
+ao_i2c_get(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       ao_mutex_get(&ao_i2c_mutex[index]);
+
+       stm_i2c->sr1 = 0;
+       stm_i2c->sr2 = 0;
+}
+
+void
+ao_i2c_put(uint8_t index)
+{
+       ao_mutex_put(&ao_i2c_mutex[index]);
+}
+
+uint8_t
+ao_i2c_start(uint8_t index, uint16_t addr)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint32_t        sr1, sr2;
+       int             t;
+
+       ao_i2c_state[index] = I2C_IDLE;
+       ao_i2c_addr[index] = addr;
+       stm_i2c->cr2 = AO_STM_I2C_CR2;
+       stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_START);
+       for (t = 0; t < I2C_TIMEOUT; t++) {
+               if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_START)))
+                       break;
+       }
+       ao_alarm(AO_MS_TO_TICKS(250));
+       cli();
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
+       ao_i2c_ev_isr(index);
+       while (ao_i2c_state[index] == I2C_IDLE)
+               if (ao_sleep(&ao_i2c_state[index]))
+                       break;
+       sei();
+       ao_clear_alarm();
+       return ao_i2c_state[index] == I2C_RUNNING;
+}
+
+static void
+ao_i2c_wait_stop(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       int     t;
+
+       for (t = 0; t < I2C_TIMEOUT; t++) {
+               if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_STOP)))
+                       break;
+               ao_yield();
+       }
+       ao_i2c_state[index] = I2C_IDLE;
+}
+
+static void
+ao_i2c_wait_addr(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       int     t;
+
+       for (t = 0; t < I2C_TIMEOUT; t++)
+               if (!(stm_i2c->sr1 & (1 << STM_I2C_SR1_ADDR)))
+                       break;
+       if (t)
+               printf ("wait_addr %d\n", t);
+}
+
+uint8_t
+ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint8_t         *b = block;
+       uint32_t        sr1;
+       uint8_t         tx_dma_index = ao_i2c_stm_info[index].tx_dma_index;
+       int             t;
+
+       /* Clear any pending ADDR bit */
+       (void) stm_i2c->sr2;
+       ao_i2c_wait_addr(index);
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_DMAEN);
+       ao_dma_set_transfer(tx_dma_index,
+                           &stm_i2c->dr,
+                           block,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+                          
+       ao_dma_start(tx_dma_index);
+       ao_alarm(1 + len);
+       cli();
+       while (!ao_dma_done[tx_dma_index])
+               if (ao_sleep(&ao_dma_done[tx_dma_index]))
+                       break;
+       ao_clear_alarm();
+       ao_dma_done_transfer(tx_dma_index);
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_ITEVTEN) | (1 << STM_I2C_CR2_ITERREN);
+       while ((stm_i2c->sr1 & (1 << STM_I2C_SR1_BTF)) == 0)
+               if (ao_sleep(&ao_i2c_state[index]))
+                       break;
+       stm_i2c->cr2 = AO_STM_I2C_CR2;
+       sei();
+       if (stop) {
+               stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+               ao_i2c_wait_stop(index);
+       }
+       return TRUE;
+}
+
+void
+ao_i2c_recv_dma_isr(int index)
+{
+       int             i;
+       struct stm_i2c  *stm_i2c = NULL;
+
+       for (i = 0; i < STM_NUM_I2C; i++)
+               if (index == ao_i2c_stm_info[i].rx_dma_index) {
+                       stm_i2c = ao_i2c_stm_info[i].stm_i2c;
+                       break;
+               }
+       if (!stm_i2c)
+               return;
+       stm_i2c->cr2 = AO_STM_I2C_CR2 | (1 << STM_I2C_CR2_LAST);
+       ao_dma_done[index] = 1;
+       ao_wakeup(&ao_dma_done[index]);
+}
+
+uint8_t
+ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       uint8_t         *b = block;
+       int             t;
+       uint8_t         ret = TRUE;
+
+       if (len == 0)
+               return TRUE;
+       if (len == 1) {
+               ao_i2c_recv_data[index] = block;
+               ao_i2c_recv_len[index] = 1;
+               stm_i2c->cr1 = AO_STM_I2C_CR1;
+
+               /* Clear any pending ADDR bit */
+               stm_i2c->sr2;
+               ao_i2c_wait_addr(index);
+
+               /* Enable interrupts to transfer the byte */
+               stm_i2c->cr2 = (AO_STM_I2C_CR2 |
+                               (1 << STM_I2C_CR2_ITEVTEN) |
+                               (1 << STM_I2C_CR2_ITERREN) |
+                               (1 << STM_I2C_CR2_ITBUFEN));
+               if (stop)
+                       stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+
+               ao_alarm(1);
+               cli();
+               while (ao_i2c_recv_len[index])
+                       if (ao_sleep(&ao_i2c_recv_len[index]))
+                               break;
+               sei();
+               ret = ao_i2c_recv_len[index] == 0;
+               ao_clear_alarm();
+       } else {
+               uint8_t         rx_dma_index = ao_i2c_stm_info[index].rx_dma_index;
+               ao_dma_set_transfer(rx_dma_index,
+                                   &stm_i2c->dr,
+                                   block,
+                                   len,
+                                   (0 << STM_DMA_CCR_MEM2MEM) |
+                                   (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                                   (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                                   (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                                   (1 << STM_DMA_CCR_MINC) |
+                                   (0 << STM_DMA_CCR_PINC) |
+                                   (0 << STM_DMA_CCR_CIRC) |
+                                   (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+               stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_ACK);
+               stm_i2c->cr2 = AO_STM_I2C_CR2 |
+                       (1 << STM_I2C_CR2_DMAEN) | (1 << STM_I2C_CR2_LAST);
+               /* Clear any pending ADDR bit */
+               (void) stm_i2c->sr2;
+               ao_i2c_wait_addr(index);
+
+               ao_dma_start(rx_dma_index);
+               ao_alarm(len);
+               cli();
+               while (!ao_dma_done[rx_dma_index])
+                       if (ao_sleep(&ao_dma_done[rx_dma_index]))
+                               break;
+               sei();
+               ao_clear_alarm();
+               ret = ao_dma_done[rx_dma_index];
+               ao_dma_done_transfer(rx_dma_index);
+               stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
+       }
+       if (stop)
+               ao_i2c_wait_stop(index);
+       return ret;
+}
+
+void
+ao_i2c_channel_init(uint8_t index)
+{
+       struct stm_i2c  *stm_i2c = ao_i2c_stm_info[index].stm_i2c;
+       int i;
+
+       /* Turn I2C off while configuring */
+       stm_i2c->cr1 = (1 << STM_I2C_CR1_SWRST);
+       for (i = 0; i < 100; i++)
+               asm("nop");
+       stm_i2c->cr1 = 0;
+       stm_i2c->cr2 = AO_STM_I2C_CR2;
+
+       (void) stm_i2c->sr1;
+       (void) stm_i2c->sr2;
+       (void) stm_i2c->dr;
+
+       stm_i2c->sr1 = 0;
+       stm_i2c->sr2 = 0;
+
+       stm_i2c->ccr = ((I2C_FAST << STM_I2C_CCR_FS) |
+                       (0 << STM_I2C_CCR_DUTY) |
+                       (I2C_CCR_HIGH << STM_I2C_CCR_CCR));
+
+       stm_i2c->trise = I2C_TRISE;
+
+       stm_i2c->cr1 = AO_STM_I2C_CR1;
+}
+
+static inline void
+i2c_pin_set(struct stm_gpio *gpio, int pin)
+{
+       stm_afr_set(gpio, pin, STM_AFR_AF4);
+       stm_ospeedr_set(gpio, pin, STM_OSPEEDR_400kHz);
+       stm_pupdr_set(gpio, pin, STM_PUPDR_PULL_UP);
+}
+
+void
+ao_i2c_init(void)
+{
+       /* All of the I2C configurations are on port B */
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+#if HAS_I2C_1
+# if I2C_1_PB6_PB7
+       i2c_pin_set(&stm_gpiob, 6);
+       i2c_pin_set(&stm_gpiob, 7);
+# else
+#  if I2C_1_PB8_PB9
+       i2c_pin_set(&stm_gpiob, 8);
+       i2c_pin_set(&stm_gpiob, 9);
+#  else
+#   error "No I2C_1 port configuration specified"
+#  endif
+# endif
+
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C1EN);
+       ao_i2c_channel_init(0);
+
+       stm_nvic_set_enable(STM_ISR_I2C1_EV_POS);
+       stm_nvic_set_priority(STM_ISR_I2C1_EV_POS, AO_STM_NVIC_MED_PRIORITY);
+       stm_nvic_set_enable(STM_ISR_I2C1_ER_POS);
+       stm_nvic_set_priority(STM_ISR_I2C1_ER_POS, AO_STM_NVIC_MED_PRIORITY);
+#endif
+
+#if HAS_I2C_2
+# if I2C_2_PB10_PB11
+       i2c_pin_set(&stm_gpiob, 10);
+       i2c_pin_set(&stm_gpiob, 11);
+# else
+#  error "No I2C_2 port configuration specified"
+# endif
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_I2C2EN);
+       ao_i2c_channel_init(1);
+
+       stm_nvic_set_enable(STM_ISR_I2C2_EV_POS);
+       stm_nvic_set_priority(STM_ISR_I2C2_EV_POS, AO_STM_NVIC_MED_PRIORITY);
+       stm_nvic_set_enable(STM_ISR_I2C2_ER_POS);
+       stm_nvic_set_priority(STM_ISR_I2C2_ER_POS, AO_STM_NVIC_MED_PRIORITY);
+#endif
+}
diff --git a/src/stm/ao_interrupt.c b/src/stm/ao_interrupt.c
new file mode 100644 (file)
index 0000000..6b4a970
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "stm32l.h"
+#include <string.h>
+
+extern void main(void);
+extern char __stack__;
+extern char __text_start__, __text_end__;
+extern char __data_start__, __data_end__;
+extern char __bss_start__, __bss_end__;
+
+/* Interrupt functions */
+
+void stm_halt_isr(void)
+{
+       for(;;);
+}
+
+void stm_ignore_isr(void)
+{
+}
+
+void start(void) {
+       memcpy(&__data_start__, &__text_end__, &__data_end__ - &__data_start__);
+       memset(&__bss_start__, '\0', &__bss_end__ - &__bss_start__);
+       main();
+}
+
+#define STRINGIFY(x) #x
+
+#define isr(name) \
+       void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_ignore_isr))
+
+#define isr_halt(name) \
+       void __attribute__ ((weak)) stm_ ## name ## _isr(void); \
+       _Pragma(STRINGIFY(weak stm_ ## name ## _isr = stm_halt_isr))
+
+isr(nmi)
+isr_halt(hardfault)
+isr_halt(memmanage)
+isr_halt(busfault)
+isr_halt(usagefault)
+isr(svc)
+isr(debugmon)
+isr(pendsv)
+isr(systick)
+isr(wwdg)
+isr(pvd)
+isr(tamper_stamp)
+isr(rtc_wkup)
+isr(flash)
+isr(rcc)
+isr(exti0)
+isr(exti1)
+isr(exti2)
+isr(exti3)
+isr(exti4)
+isr(dma1_channel1)
+isr(dma1_channel2)
+isr(dma1_channel3)
+isr(dma1_channel4)
+isr(dma1_channel5)
+isr(dma1_channel6)
+isr(dma1_channel7)
+isr(adc1)
+isr(usb_hp)
+isr(usb_lp)
+isr(dac)
+isr(comp)
+isr(exti9_5)
+isr(lcd)
+isr(tim9)
+isr(tim10)
+isr(tim11)
+isr(tim2)
+isr(tim3)
+isr(tim4)
+isr(i2c1_ev)
+isr(i2c1_er)
+isr(i2c2_ev)
+isr(i2c2_er)
+isr(spi1)
+isr(spi2)
+isr(usart1)
+isr(usart2)
+isr(usart3)
+isr(exti15_10)
+isr(rtc_alarm)
+isr(usb_fs_wkup)
+isr(tim6)
+isr(tim7)
+
+#define i(addr,name)   [(addr)/4] = stm_ ## name ## _isr
+
+__attribute__ ((section(".interrupt")))
+const void *stm_interrupt_vector[] = {
+       [0] = &__stack__,
+       [1] = start,
+       i(0x08, nmi),
+       i(0x0c, hardfault),
+       i(0x10, memmanage),
+       i(0x14, busfault),
+       i(0x18, usagefault),
+       i(0x2c, svc),
+       i(0x30, debugmon),
+       i(0x38, pendsv),
+       i(0x3c, systick),
+       i(0x40, wwdg),
+       i(0x44, pvd),
+       i(0x48, tamper_stamp),
+       i(0x4c, rtc_wkup),
+       i(0x50, flash),
+       i(0x54, rcc),
+       i(0x58, exti0),
+       i(0x5c, exti1),
+       i(0x60, exti2),
+       i(0x64, exti3),
+       i(0x68, exti4),
+       i(0x6c, dma1_channel1),
+       i(0x70, dma1_channel2),
+       i(0x74, dma1_channel3),
+       i(0x78, dma1_channel4),
+       i(0x7c, dma1_channel5),
+       i(0x80, dma1_channel6),
+       i(0x84, dma1_channel7),
+       i(0x88, adc1),
+       i(0x8c, usb_hp),
+       i(0x90, usb_lp),
+       i(0x94, dac),
+       i(0x98, comp),
+       i(0x9c, exti9_5),
+       i(0xa0, lcd),
+       i(0xa4, tim9),
+       i(0xa8, tim10),
+       i(0xac, tim11),
+       i(0xb0, tim2),
+       i(0xb4, tim3),
+       i(0xb8, tim4),
+       i(0xbc, i2c1_ev),
+       i(0xc0, i2c1_er),
+       i(0xc4, i2c2_ev),
+       i(0xc8, i2c2_er),
+       i(0xcc, spi1),
+       i(0xd0, spi2),
+       i(0xd4, usart1),
+       i(0xd8, usart2),
+       i(0xdc, usart3),
+       i(0xe0, exti15_10),
+       i(0xe4, rtc_alarm),
+       i(0xe8, usb_fs_wkup),
+       i(0xec, tim6),
+       i(0xf0, tim7),
+};
diff --git a/src/stm/ao_lcd_font.c b/src/stm/ao_lcd_font.c
new file mode 100644 (file)
index 0000000..0d7d87c
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+static const uint16_t ao_lcd_font[] = {
+#include "ao_lcd_font.h"
+};
+
+/*
+        -----          0
+       |\ | /|         1 2 3 4 5
+       | \|/ | @               14
+        -- --          6 7
+       | /|\ |         8 9 10 11 12    
+       |/ | \| @               14
+        -----          13
+               @               15
+*/
+
+static const uint8_t   ao_bit_src[] = {
+        8,  7,  5,  6,
+       13, 12,  0,  1,
+       10, 14,  4,  9,
+       11, 15,  3,  2
+};
+
+static const uint8_t   ao_bit_dst[6][4] = {
+       {  0,  1, 28, 29 },
+       {  2,  7, 26, 27 },
+       {  8,  9, 24, 25 },
+       { 10, 11, 20, 21 },
+       { 12, 13, 18, 19 },
+       { 14, 15, 17, 16 },
+};
+
+static void
+ao_draw_glyph(uint8_t pos, uint16_t glyph) {
+       uint8_t         col, seg, src, dst;
+       uint32_t        ram;
+
+       for (col = 0; col < 4; col++) {
+               for (seg = 0; seg < 4; seg++) {
+                       src = ao_bit_src[(col << 2) | seg];
+                       dst = ao_bit_dst[pos][seg];
+                       ram = stm_lcd.ram[col * 2];
+                       ram &= ~(1 << dst);
+                       ram |= (uint32_t) ((glyph >> src) & 1) << dst;
+                       stm_lcd.ram[col*2] = ram;
+               }
+       }
+}
+
+#define AO_LCD_FONT_COLON      (1 << 14)
+#define AO_LCD_FONT_DECIMAL    (1 << 15)
+
+void
+ao_lcd_font_char(uint8_t pos, char c, uint16_t flags) {
+       if (pos > 5)
+               pos = 5;
+       if (c < ' ' || c > 127)
+               c = 127;
+       ao_draw_glyph(pos, ao_lcd_font[c - ' '] | flags);
+}
+
+void
+ao_lcd_font_string(char *s) {
+       uint8_t pos = 0;
+       uint16_t flags;
+       char c;
+
+       while (pos < 6 && (c = *s++)) {
+               flags = 0;
+               for (;;) {
+                       if (*s == ':')
+                               flags |= AO_LCD_FONT_COLON;
+                       else if (*s == '.')
+                               flags |= AO_LCD_FONT_DECIMAL;
+                       else
+                               break;
+                       s++;
+               }
+               ao_lcd_font_char(pos++, c, flags);
+       }
+       while (pos < 6)
+               ao_lcd_font_char(pos++, ' ', 0);
+       stm_lcd.sr = (1 << STM_LCD_SR_UDR);
+}
+
+static void
+ao_lcd_font_text(void)
+{
+       char    string[20];
+       uint8_t c = 0;
+       ao_cmd_white();
+       while (ao_cmd_lex_c != '\n' && c < sizeof (string) - 1) {
+               string[c++] = ao_cmd_lex_c;
+               ao_cmd_lex();
+       }
+       string[c++] = '\0';
+       ao_lcd_font_string(string);
+}
+
+const struct ao_cmds ao_lcd_font_cmds[] = {
+       { ao_lcd_font_text,     "t <string>\0Write <string> to LCD" },
+       { 0, NULL }
+};
+
+void
+ao_lcd_font_init(void)
+{
+       ao_cmd_register(ao_lcd_font_cmds);
+}
+       
diff --git a/src/stm/ao_lcd_font.h b/src/stm/ao_lcd_font.h
new file mode 100644 (file)
index 0000000..08adc9a
--- /dev/null
@@ -0,0 +1,1152 @@
+       [0] = 0x0000,
+/*
+CHAR 32 ' '
+              
+              
+              
+              
+              
+              
+              
+*/
+
+       [1] = 0x0102,
+/*
+CHAR 33 '!'
+              
+       |      
+       |      
+              
+       |      
+       |      
+              
+*/
+
+       [2] = 0x000a,
+/*
+CHAR 34 '"'
+              
+       |  |   
+       |  |   
+              
+              
+              
+              
+*/
+
+       [3] = 0x05e8,
+/*
+CHAR 35 '#'
+              
+          |  |
+          |  |
+        -- -- 
+       |  |   
+       |  |   
+              
+*/
+
+       [4] = 0x34cb,
+/*
+CHAR 36 '$'
+        ----- 
+       |  |   
+       |  |   
+        -- -- 
+          |  |
+          |  |
+        ----- 
+*/
+
+       [5] = 0x1212,
+/*
+CHAR 37 '%'
+              
+       |    / 
+       |   /  
+              
+         /   |
+        /    |
+              
+*/
+
+       [6] = 0x2955,
+/*
+CHAR 38 '&'
+        ----- 
+        \   / 
+         \ /  
+        --    
+       |   \  
+       |    \ 
+        ----- 
+*/
+
+       [7] = 0x0008,
+/*
+CHAR 39 '''
+              
+          |   
+          |   
+              
+              
+              
+              
+*/
+
+       [8] = 0x2103,
+/*
+CHAR 40 '('
+        ----- 
+       |      
+       |      
+              
+       |      
+       |      
+        ----- 
+*/
+
+       [9] = 0x3021,
+/*
+CHAR 41 ')'
+        ----- 
+             |
+             |
+              
+             |
+             |
+        ----- 
+*/
+
+       [10] = 0x0e1c,
+/*
+CHAR 42 '*'
+              
+        \ | / 
+         \|/  
+              
+         /|\  
+        / | \ 
+              
+*/
+
+       [11] = 0x04c8,
+/*
+CHAR 43 '+'
+              
+          |   
+          |   
+        -- -- 
+          |   
+          |   
+              
+*/
+
+       [12] = 0x0200,
+/*
+CHAR 44 ','
+              
+              
+              
+              
+         /    
+        /     
+              
+*/
+
+       [13] = 0x00c0,
+/*
+CHAR 45 '-'
+              
+              
+              
+        -- -- 
+              
+              
+              
+*/
+
+       [14] = 0x0800,
+/*
+CHAR 46 '.'
+              
+              
+              
+              
+           \  
+            \ 
+              
+*/
+
+       [15] = 0x0210,
+/*
+CHAR 47 '/'
+              
+            / 
+           /  
+              
+         /    
+        /     
+              
+*/
+
+       [16] = 0x3333,
+/*
+CHAR 48 '0'
+        ----- 
+       |    /|
+       |   / |
+              
+       | /   |
+       |/    |
+        ----- 
+*/
+
+       [17] = 0x1030,
+/*
+CHAR 49 '1'
+              
+            /|
+           / |
+              
+             |
+             |
+              
+*/
+
+       [18] = 0x21e1,
+/*
+CHAR 50 '2'
+        ----- 
+             |
+             |
+        -- -- 
+       |      
+       |      
+        ----- 
+*/
+
+       [19] = 0x30a1,
+/*
+CHAR 51 '3'
+        ----- 
+             |
+             |
+           -- 
+             |
+             |
+        ----- 
+*/
+
+       [20] = 0x10e2,
+/*
+CHAR 52 '4'
+              
+       |     |
+       |     |
+        -- -- 
+             |
+             |
+              
+*/
+
+       [21] = 0x30c3,
+/*
+CHAR 53 '5'
+        ----- 
+       |      
+       |      
+        -- -- 
+             |
+             |
+        ----- 
+*/
+
+       [22] = 0x31c3,
+/*
+CHAR 54 '6'
+        ----- 
+       |      
+       |      
+        -- -- 
+       |     |
+       |     |
+        ----- 
+*/
+
+       [23] = 0x0411,
+/*
+CHAR 55 '7'
+        ----- 
+            / 
+           /  
+              
+          |   
+          |   
+              
+*/
+
+       [24] = 0x31e3,
+/*
+CHAR 56 '8'
+        ----- 
+       |     |
+       |     |
+        -- -- 
+       |     |
+       |     |
+        ----- 
+*/
+
+       [25] = 0x10e3,
+/*
+CHAR 57 '9'
+        ----- 
+       |     |
+       |     |
+        -- -- 
+             |
+             |
+              
+*/
+
+       [26] = 0x0408,
+/*
+CHAR 58 ':'
+              
+          |   
+          |   
+              
+          |   
+          |   
+              
+*/
+
+       [27] = 0x0208,
+/*
+CHAR 59 ';'
+              
+          |   
+          |   
+              
+         /    
+        /     
+              
+*/
+
+       [28] = 0x0810,
+/*
+CHAR 60 '<'
+              
+            / 
+           /  
+              
+           \  
+            \ 
+              
+*/
+
+       [29] = 0x20c0,
+/*
+CHAR 61 '='
+              
+              
+              
+        -- -- 
+              
+              
+        ----- 
+*/
+
+       [30] = 0x0204,
+/*
+CHAR 62 '>'
+              
+        \     
+         \    
+              
+         /    
+        /     
+              
+*/
+
+       [31] = 0x0413,
+/*
+CHAR 63 '?'
+        ----- 
+       |    / 
+       |   /  
+              
+          |   
+          |   
+              
+*/
+
+       [32] = 0x39b3,
+/*
+CHAR 64 '@'
+        ----- 
+       |    /|
+       |   / |
+           -- 
+       |   \ |
+       |    \|
+        ----- 
+*/
+
+       [33] = 0x11e3,
+/*
+CHAR 65 'A'
+        ----- 
+       |     |
+       |     |
+        -- -- 
+       |     |
+       |     |
+              
+*/
+
+       [34] = 0x34a9,
+/*
+CHAR 66 'B'
+        ----- 
+          |  |
+          |  |
+           -- 
+          |  |
+          |  |
+        ----- 
+*/
+
+       [35] = 0x2103,
+/*
+CHAR 67 'C'
+        ----- 
+       |      
+       |      
+              
+       |      
+       |      
+        ----- 
+*/
+
+       [36] = 0x3429,
+/*
+CHAR 68 'D'
+        ----- 
+          |  |
+          |  |
+              
+          |  |
+          |  |
+        ----- 
+*/
+
+       [37] = 0x2143,
+/*
+CHAR 69 'E'
+        ----- 
+       |      
+       |      
+        --    
+       |      
+       |      
+        ----- 
+*/
+
+       [38] = 0x0143,
+/*
+CHAR 70 'F'
+        ----- 
+       |      
+       |      
+        --    
+       |      
+       |      
+              
+*/
+
+       [39] = 0x3183,
+/*
+CHAR 71 'G'
+        ----- 
+       |      
+       |      
+           -- 
+       |     |
+       |     |
+        ----- 
+*/
+
+       [40] = 0x11e2,
+/*
+CHAR 72 'H'
+              
+       |     |
+       |     |
+        -- -- 
+       |     |
+       |     |
+              
+*/
+
+       [41] = 0x2409,
+/*
+CHAR 73 'I'
+        ----- 
+          |   
+          |   
+              
+          |   
+          |   
+        ----- 
+*/
+
+       [42] = 0x3120,
+/*
+CHAR 74 'J'
+              
+             |
+             |
+              
+       |     |
+       |     |
+        ----- 
+*/
+
+       [43] = 0x0952,
+/*
+CHAR 75 'K'
+              
+       |    / 
+       |   /  
+        --    
+       |   \  
+       |    \ 
+              
+*/
+
+       [44] = 0x2102,
+/*
+CHAR 76 'L'
+              
+       |      
+       |      
+              
+       |      
+       |      
+        ----- 
+*/
+
+       [45] = 0x1136,
+/*
+CHAR 77 'M'
+              
+       |\   /|
+       | \ / |
+              
+       |     |
+       |     |
+              
+*/
+
+       [46] = 0x1926,
+/*
+CHAR 78 'N'
+              
+       |\    |
+       | \   |
+              
+       |   \ |
+       |    \|
+              
+*/
+
+       [47] = 0x3123,
+/*
+CHAR 79 'O'
+        ----- 
+       |     |
+       |     |
+              
+       |     |
+       |     |
+        ----- 
+*/
+
+       [48] = 0x01e3,
+/*
+CHAR 80 'P'
+        ----- 
+       |     |
+       |     |
+        -- -- 
+       |      
+       |      
+              
+*/
+
+       [49] = 0x3923,
+/*
+CHAR 81 'Q'
+        ----- 
+       |     |
+       |     |
+              
+       |   \ |
+       |    \|
+        ----- 
+*/
+
+       [50] = 0x09e3,
+/*
+CHAR 82 'R'
+        ----- 
+       |     |
+       |     |
+        -- -- 
+       |   \  
+       |    \ 
+              
+*/
+
+       [51] = 0x3085,
+/*
+CHAR 83 'S'
+        ----- 
+        \     
+         \    
+           -- 
+             |
+             |
+        ----- 
+*/
+
+       [52] = 0x0409,
+/*
+CHAR 84 'T'
+        ----- 
+          |   
+          |   
+              
+          |   
+          |   
+              
+*/
+
+       [53] = 0x3122,
+/*
+CHAR 85 'U'
+              
+       |     |
+       |     |
+              
+       |     |
+       |     |
+        ----- 
+*/
+
+       [54] = 0x0312,
+/*
+CHAR 86 'V'
+              
+       |    / 
+       |   /  
+              
+       | /    
+       |/     
+              
+*/
+
+       [55] = 0x1b22,
+/*
+CHAR 87 'W'
+              
+       |     |
+       |     |
+              
+       | / \ |
+       |/   \|
+              
+*/
+
+       [56] = 0x0a14,
+/*
+CHAR 88 'X'
+              
+        \   / 
+         \ /  
+              
+         / \  
+        /   \ 
+              
+*/
+
+       [57] = 0x0414,
+/*
+CHAR 89 'Y'
+              
+        \   / 
+         \ /  
+              
+          |   
+          |   
+              
+*/
+
+       [58] = 0x2211,
+/*
+CHAR 90 'Z'
+        ----- 
+            / 
+           /  
+              
+         /    
+        /     
+        ----- 
+*/
+
+       [59] = 0x2103,
+/*
+CHAR 91 '['
+        ----- 
+       |      
+       |      
+              
+       |      
+       |      
+        ----- 
+*/
+
+       [60] = 0x0804,
+/*
+CHAR 92 '\'
+              
+        \     
+         \    
+              
+           \  
+            \ 
+              
+*/
+
+       [61] = 0x3021,
+/*
+CHAR 93 ']'
+        ----- 
+             |
+             |
+              
+             |
+             |
+        ----- 
+*/
+
+       [62] = 0x0023,
+/*
+CHAR 94 '^'
+        ----- 
+       |     |
+       |     |
+              
+              
+              
+              
+*/
+
+       [63] = 0x2000,
+/*
+CHAR 95 '_'
+              
+              
+              
+              
+              
+              
+        ----- 
+*/
+
+       [64] = 0x0004,
+/*
+CHAR 96 '`'
+              
+        \     
+         \    
+              
+              
+              
+              
+*/
+
+       [65] = 0x2540,
+/*
+CHAR 97 'a'
+              
+              
+              
+        --    
+       |  |   
+       |  |   
+        ----- 
+*/
+
+       [66] = 0x2942,
+/*
+CHAR 98 'b'
+              
+       |      
+       |      
+        --    
+       |   \  
+       |    \ 
+        ----- 
+*/
+
+       [67] = 0x21c0,
+/*
+CHAR 99 'c'
+              
+              
+              
+        -- -- 
+       |      
+       |      
+        ----- 
+*/
+
+       [68] = 0x32a0,
+/*
+CHAR 100 'd'
+              
+             |
+             |
+           -- 
+         /   |
+        /    |
+        ----- 
+*/
+
+       [69] = 0x2340,
+/*
+CHAR 101 'e'
+              
+              
+              
+        --    
+       | /    
+       |/     
+        ----- 
+*/
+
+       [70] = 0x0143,
+/*
+CHAR 102 'f'
+        ----- 
+       |      
+       |      
+        --    
+       |      
+       |      
+              
+*/
+
+       [71] = 0x10a5,
+/*
+CHAR 103 'g'
+        ----- 
+        \    |
+         \   |
+           -- 
+             |
+             |
+              
+*/
+
+       [72] = 0x11c2,
+/*
+CHAR 104 'h'
+              
+       |      
+       |      
+        -- -- 
+       |     |
+       |     |
+              
+*/
+
+       [73] = 0x0400,
+/*
+CHAR 105 'i'
+              
+              
+              
+              
+          |   
+          |   
+              
+*/
+
+       [74] = 0x3000,
+/*
+CHAR 106 'j'
+              
+              
+              
+              
+             |
+             |
+        ----- 
+*/
+
+       [75] = 0x0c88,
+/*
+CHAR 107 'k'
+              
+          |   
+          |   
+           -- 
+          |\  
+          | \ 
+              
+*/
+
+       [76] = 0x0408,
+/*
+CHAR 108 'l'
+              
+          |   
+          |   
+              
+          |   
+          |   
+              
+*/
+
+       [77] = 0x15c0,
+/*
+CHAR 109 'm'
+              
+              
+              
+        -- -- 
+       |  |  |
+       |  |  |
+              
+*/
+
+       [78] = 0x0940,
+/*
+CHAR 110 'n'
+              
+              
+              
+        --    
+       |   \  
+       |    \ 
+              
+*/
+
+       [79] = 0x31c0,
+/*
+CHAR 111 'o'
+              
+              
+              
+        -- -- 
+       |     |
+       |     |
+        ----- 
+*/
+
+       [80] = 0x0146,
+/*
+CHAR 112 'p'
+              
+       |\     
+       | \    
+        --    
+       |      
+       |      
+              
+*/
+
+       [81] = 0x10b0,
+/*
+CHAR 113 'q'
+              
+            /|
+           / |
+           -- 
+             |
+             |
+              
+*/
+
+       [82] = 0x0140,
+/*
+CHAR 114 'r'
+              
+              
+              
+        --    
+       |      
+       |      
+              
+*/
+
+       [83] = 0x2880,
+/*
+CHAR 115 's'
+              
+              
+              
+           -- 
+           \  
+            \ 
+        ----- 
+*/
+
+       [84] = 0x2142,
+/*
+CHAR 116 't'
+              
+       |      
+       |      
+        --    
+       |      
+       |      
+        ----- 
+*/
+
+       [85] = 0x3100,
+/*
+CHAR 117 'u'
+              
+              
+              
+              
+       |     |
+       |     |
+        ----- 
+*/
+
+       [86] = 0x0300,
+/*
+CHAR 118 'v'
+              
+              
+              
+              
+       | /    
+       |/     
+              
+*/
+
+       [87] = 0x1b00,
+/*
+CHAR 119 'w'
+              
+              
+              
+              
+       | / \ |
+       |/   \|
+              
+*/
+
+       [88] = 0x0a14,
+/*
+CHAR 120 'x'
+              
+        \   / 
+         \ /  
+              
+         / \  
+        /   \ 
+              
+*/
+
+       [89] = 0x3800,
+/*
+CHAR 121 'y'
+              
+              
+              
+              
+           \ |
+            \|
+        ----- 
+*/
+
+       [90] = 0x2240,
+/*
+CHAR 122 'z'
+              
+              
+              
+        --    
+         /    
+        /     
+        ----- 
+*/
+
+       [91] = 0x2245,
+/*
+CHAR 123 '{'
+        ----- 
+        \     
+         \    
+        --    
+         /    
+        /     
+        ----- 
+*/
+
+       [92] = 0x0408,
+/*
+CHAR 124 '|'
+              
+          |   
+          |   
+              
+          |   
+          |   
+              
+*/
+
+       [93] = 0x2891,
+/*
+CHAR 125 '}'
+        ----- 
+            / 
+           /  
+           -- 
+           \  
+            \ 
+        ----- 
+*/
+
+       [94] = 0x000e,
+/*
+CHAR 126 '~'
+              
+       |\ |   
+       | \|   
+              
+              
+              
+              
+*/
+
+       [95] = 0x3fff,
+/*
+CHAR 127 'DEL'
+        ----- 
+       |\ | /|
+       | \|/ |
+        -- -- 
+       | /|\ |
+       |/ | \|
+        ----- 
+*/
+
diff --git a/src/stm/ao_lcd_stm.c b/src/stm/ao_lcd_stm.c
new file mode 100644 (file)
index 0000000..0f9a8eb
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_lcd_stm.h>
+
+struct ao_lcd_segment {
+       uint8_t reg;
+       uint8_t bit;
+};
+
+#define A      0
+#define B      1
+#define C      2
+#define D      3
+#define E      4
+
+static struct stm_gpio *gpios[] = {
+       &stm_gpioa,
+       &stm_gpiob,
+       &stm_gpioc,
+       &stm_gpiod,
+       &stm_gpioe
+};
+
+static inline int ao_lcd_stm_seg_enabled(int seg) {
+       if (seg < 32)
+               return (AO_LCD_STM_SEG_ENABLED_0 >> seg) & 1;
+       else
+               return (AO_LCD_STM_SEG_ENABLED_1 >> (seg - 32)) & 1;
+}
+
+static inline int ao_lcd_stm_com_enabled(int com) {
+       return (AO_LCD_STM_COM_ENABLED >> com) & 1;
+}
+
+#define AO_LCD_STM_GPIOA_SEGS_0        (               \
+               (1 << 0) |                      \
+               (1 << 1) |                      \
+               (1 << 2) |                      \
+               (1 << 3) |                      \
+               (1 << 4) |                      \
+               (1 << 17))
+
+#define AO_LCD_STM_GPIOA_SEGS_1 0
+
+#define AO_LCD_STM_USES_GPIOA  (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
+                                   (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
+
+
+#define AO_LCD_STM_GPIOB_SEGS_0        (               \
+               (1 << 5) |                      \
+               (1 << 6) |                      \
+               (1 << 7) |                      \
+               (1 << 8) |                      \
+               (1 << 9) |                      \
+               (1 << 10) |                     \
+               (1 << 11) |                     \
+               (1 << 12) |                     \
+               (1 << 13) |                     \
+               (1 << 14) |                     \
+               (1 << 15) |                     \
+               (1 << 16))
+
+#define AO_LCD_STM_GPIOB_SEGS_1 0
+
+#if AO_LCD_28_ON_C
+
+#define AO_LCD_STM_GPIOC_28_SEGS       (       \
+               (1 << 28) |                     \
+               (1 << 29) |                     \
+               (1 << 30))
+
+#define AO_LCD_STM_GPIOD_28_SEGS       (       \
+               (1 << 31))
+
+#else
+#define AO_LCD_STM_GPIOC_28_SEGS       0
+
+#define AO_LCD_STM_GPIOD_28_SEGS       (       \
+               (1 << 28) |                     \
+               (1 << 29) |                     \
+               (1 << 30) |                     \
+               (1 << 31))
+#endif
+
+#define AO_LCD_STM_GPIOC_SEGS_0        (               \
+               (1 << 18) |                     \
+               (1 << 19) |                     \
+               (1 << 20) |                     \
+               (1 << 21) |                     \
+               (1 << 22) |                     \
+               (1 << 23) |                     \
+               (1 << 24) |                     \
+               (1 << 25) |                     \
+               (1 << 26) |                     \
+               (1 << 27) |                     \
+               AO_LCD_STM_GPIOC_28_SEGS)
+
+#define AO_LCD_STM_GPIOC_SEGS_1 (              \
+               (1 << (40 - 32)) |              \
+               (1 << (41 - 32)) |              \
+               (1 << (42 - 32)))
+
+#define AO_LCD_STM_GPIOD_SEGS_0        (               \
+               AO_LCD_STM_GPIOD_28_SEGS)
+
+#define AO_LCD_STM_GPIOD_SEGS_1 (              \
+               (1 << (32 - 32)) |              \
+               (1 << (33 - 32)) |              \
+               (1 << (34 - 32)) |              \
+               (1 << (35 - 32)) |              \
+               (1 << (43 - 32)))
+
+#define AO_LCD_STM_GPIOE_SEGS_0        0
+
+#define AO_LCD_STM_GPIOE_SEGS_1 (              \
+               (1 << (36 - 32)) |              \
+               (1 << (37 - 32)) |              \
+               (1 << (38 - 32)) |              \
+               (1 << (39 - 32)))
+
+#define AO_LCD_STM_USES_GPIOA  (!!((AO_LCD_STM_GPIOA_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
+                                   (AO_LCD_STM_GPIOA_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
+
+#define AO_LCD_STM_USES_GPIOB  (!!((AO_LCD_STM_GPIOB_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
+                                   (AO_LCD_STM_GPIOB_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
+
+
+#define AO_LCD_STM_USES_GPIOC  (!!((AO_LCD_STM_GPIOC_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
+                                   (AO_LCD_STM_GPIOC_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
+
+
+#define AO_LCD_STM_USES_GPIOD  (!!((AO_LCD_STM_GPIOD_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
+                                   (AO_LCD_STM_GPIOD_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
+
+#define AO_LCD_STM_USES_GPIOE  (!!((AO_LCD_STM_GPIOE_SEGS_0 & AO_LCD_STM_SEG_ENABLED_0) | \
+                                   (AO_LCD_STM_GPIOE_SEGS_1 & AO_LCD_STM_SEG_ENABLED_1)))
+
+
+static const struct ao_lcd_segment segs[] = {
+       { A, 1 },       /* 0 */
+       { A, 2 },
+       { A, 3 },
+       { A, 6 },
+               
+       { A, 7 },       /* 4 */
+       { B, 0 },
+       { B, 1 },
+       { B, 3 },
+
+       { B, 4 },       /* 8 */
+       { B, 5 },
+       { B, 10 },
+       { B, 11 },
+
+       { B, 12 },      /* 12 */
+       { B, 13 },
+       { B, 14 },
+       { B, 15 },
+
+       { B, 8 },       /* 16 */
+       { A, 15 },
+       { C, 0 },
+       { C, 1 },
+
+       { C, 2 },       /* 20 */
+       { C, 3 },
+       { C, 4 },
+       { C, 5 },
+
+       { C, 6 },       /* 24 */
+       { C, 7 },
+       { C, 8 },
+       { C, 9 },
+
+#if AO_LCD_28_ON_C
+       { C, 10 },      /* 28 */
+       { C, 11 },
+       { C, 12 },
+       { D, 2 },
+#else
+       { D, 8 },       /* 28 */
+       { D, 9 },
+       { D, 10 },
+       { D, 11 },
+#endif
+       { D, 12 },      /* 32 */
+       { D, 13 },
+       { D, 14 },
+       { D, 15 },
+               
+       { E, 0 },       /* 36 */
+       { E, 1 },
+       { E, 2 },
+       { E, 3 },
+
+       { C, 10 },      /* 40 */
+       { C, 11 },
+       { C, 12 },
+       { D, 2 },
+};
+
+static const struct ao_lcd_segment coms[] = {
+       { A, 8 },       /* 0 */
+       { A, 9 },       /* 1 */
+       { A, 10 },      /* 2 */
+       { B, 9 },       /* 3 */
+       { C, 10 },      /* 4 */
+       { C, 11 },      /* 5 */
+       { C, 12 },      /* 6 */
+};
+
+#define NSEG   (sizeof segs/sizeof segs[0])
+#define NCOM   (sizeof coms/sizeof coms[0])
+
+static uint8_t ao_lcd_update_active;
+
+void
+stm_lcd_isr(void)
+{
+       if (stm_lcd.sr & (1 << STM_LCD_SR_UDD)) {
+               stm_lcd.clr = (1 << STM_LCD_CLR_UDDC);
+               if (ao_lcd_update_active) {
+                       ao_lcd_update_active = 0;
+                       ao_wakeup(&ao_lcd_update_active);
+               }
+       }
+}
+
+
+static void
+ao_lcd_stm_fcr_sync(void)
+{
+       while ((stm_lcd.sr & (1 << STM_LCD_SR_FCRSF)) == 0)
+               asm("nop");
+}
+
+void
+ao_lcd_flush(void)
+{
+       cli();
+       ao_lcd_update_active = 1;
+       stm_lcd.sr = (1 << STM_LCD_SR_UDR);
+       while (ao_lcd_update_active)
+               ao_sleep(&ao_lcd_update_active);
+       sei();
+}
+
+void
+ao_lcd_clear(void)
+{
+       uint8_t i;
+
+       for (i = 0; i < sizeof (stm_lcd.ram) / 4; i++)
+               stm_lcd.ram[i] = 0;
+       ao_lcd_flush();
+}
+
+void
+ao_lcd_set(uint8_t digit, uint8_t segment, uint8_t value)
+{
+       uint8_t n;
+
+       if (digit >= NCOM)
+               digit = NCOM-1;
+       if (segment >= NSEG)
+               segment = NSEG-1;
+
+       n = (segment >> 5) & 1;
+       if (value)
+               stm_lcd.ram[digit * 2 + n] |= (1 << (segment & 0x1f));
+       else
+               stm_lcd.ram[digit * 2 + n] &= ~(1 << (segment & 0x1f));
+}
+
+#if 0
+static void
+ao_lcd_stm_seg_set(void)
+{
+       int     com, seg, val;
+       int     n, bit;
+       ao_cmd_decimal();
+       com = ao_cmd_lex_i;
+       ao_cmd_decimal();
+       seg = ao_cmd_lex_u32;
+       ao_cmd_decimal();
+       val = ao_cmd_lex_i;
+       printf ("com: %d seg: %d val: %d\n", com, seg, val);
+       ao_lcd_set(com, seg, val);
+       ao_lcd_flush();
+}
+
+static const struct ao_cmds ao_lcd_stm_cmds[] = {
+       { ao_lcd_stm_seg_set,   "s <com> <seg> <value>\0Set LCD segment" },
+       { ao_lcd_clear,         "C\0Clear LCD" },
+       { 0, NULL },
+};
+#endif
+
+void
+ao_lcd_stm_init(void)
+{
+       int s, c;
+       int r;
+       uint32_t        csr;
+
+       stm_rcc.ahbenr |= ((AO_LCD_STM_USES_GPIOA << STM_RCC_AHBENR_GPIOAEN) |
+                          (AO_LCD_STM_USES_GPIOB << STM_RCC_AHBENR_GPIOBEN) |
+                          (AO_LCD_STM_USES_GPIOC << STM_RCC_AHBENR_GPIOCEN) |
+                          (AO_LCD_STM_USES_GPIOD << STM_RCC_AHBENR_GPIODEN) |
+                          (AO_LCD_STM_USES_GPIOE << STM_RCC_AHBENR_GPIOEEN));
+
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_LCDEN);
+
+       /* Turn on the LSI clock */
+       if ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0) {
+               stm_rcc.csr |= (1 << STM_RCC_CSR_LSION);
+               while ((stm_rcc.csr & (1 << STM_RCC_CSR_LSIRDY)) == 0)
+                       asm("nop");
+       }
+
+       /* Enable RTC clock config (required to change the RTC/LCD clock */
+
+       stm_pwr.cr |= (1 << STM_PWR_CR_DBP);
+
+       /* Configure the LCDCLK - use the LSI clock */
+
+       csr = stm_rcc.csr;
+       csr &= ~(STM_RCC_CSR_RTCSEL_MASK << STM_RCC_CSR_RTCSEL);
+       csr |= (STM_RCC_CSR_RTCSEL_LSI << STM_RCC_CSR_RTCSEL);
+       stm_rcc.csr = csr;
+
+       for (s = 0; s < NSEG; s++) {
+               uint8_t reg = segs[s].reg;
+               uint8_t bit = segs[s].bit;
+                       
+               if (ao_lcd_stm_seg_enabled(s)) {
+                       stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
+                       stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
+               }
+       }
+
+       for (c = 0; c < NCOM; c++) {
+               uint8_t reg = coms[c].reg;
+               uint8_t bit = coms[c].bit;
+                       
+               if (ao_lcd_stm_com_enabled(c)) {
+                       stm_moder_set(gpios[reg], bit, STM_MODER_ALTERNATE);
+                       stm_afr_set(gpios[reg], bit, STM_AFR_AF11);
+               }
+       }
+
+       /* Disable the LCD */
+       stm_lcd.cr = 0;
+
+       /* duty cycle 1/3, radio 352, frame rate about 33Hz */
+       stm_lcd.fcr = ((STM_LCD_FCR_PS_8 << STM_LCD_FCR_PS) |
+                      (STM_LCD_FCR_DIV_20 << STM_LCD_FCR_DIV) |
+                      (7 << STM_LCD_FCR_CC) |
+                      (0 << STM_LCD_FCR_DEAD) |
+                      (1 << STM_LCD_FCR_PON) |
+                      (1 << STM_LCD_FCR_UDDIE) |
+                      (0 << STM_LCD_FCR_SOFIE) |
+                      (1 << STM_LCD_FCR_HD));
+
+       ao_lcd_stm_fcr_sync();
+
+       /* Program desired DUTY in LCD_CR */
+       /* Program desired BIAS in LCD_CR */
+       /* Enable mux seg */
+       /* Internal voltage source */
+       stm_lcd.cr = ((AO_LCD_DUTY << STM_LCD_CR_DUTY) |
+                     (STM_LCD_CR_BIAS_1_2 << STM_LCD_CR_BIAS) |
+                     (0 << STM_LCD_CR_VSEL) |
+                     (0 << STM_LCD_CR_MUX_SEG));
+
+       ao_lcd_stm_fcr_sync();
+
+       /* Enable the display (LCDEN bit in LCD_CR) */
+       stm_lcd.cr |= (1 << STM_LCD_CR_LCDEN);
+
+       while ((stm_lcd.sr & (1 << STM_LCD_SR_RDY)) == 0)
+               asm("nop");
+
+       /* Load initial data into LCD_RAM and set the
+        * UDR bit in the LCD_SR register */
+
+       /* Program desired frame rate (PS and DIV bits in LCD_FCR) */
+
+       /* Program the contrast (CC bits in LCD_FCR) */
+
+       /* Program optional features (BLINK, BLINKF, PON, DEAD, HD) */
+
+       /* Program the required interrupts */
+       stm_nvic_set_enable(STM_ISR_LCD_POS);
+       stm_nvic_set_priority(STM_ISR_LCD_POS, AO_STM_NVIC_LOW_PRIORITY);
+
+       /* All done */
+#if 0
+       ao_cmd_register(ao_lcd_stm_cmds);
+#endif
+}
diff --git a/src/stm/ao_lcd_stm.h b/src/stm/ao_lcd_stm.h
new file mode 100644 (file)
index 0000000..1466754
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LCD_STM_H_
+#define _AO_LCD_STM_H_
+
+void
+ao_lcd_set(uint8_t digit, uint8_t segment, uint8_t value);
+
+void
+ao_lcd_clear(void);
+
+void
+ao_lcd_flush(void);
+
+#endif /* _AO_LCD_STM_H_ */
diff --git a/src/stm/ao_led.c b/src/stm/ao_led.c
new file mode 100644 (file)
index 0000000..ee313b6
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+__pdata uint16_t ao_led_enable;
+
+void
+ao_led_on(uint16_t colors)
+{
+       LED_PORT->bsrr = (colors & ao_led_enable);
+}
+
+void
+ao_led_off(uint16_t colors)
+{
+       LED_PORT->bsrr = (uint32_t) (colors & ao_led_enable) << 16;
+}
+
+void
+ao_led_set(uint16_t colors)
+{
+       uint16_t        on = colors & ao_led_enable;
+       uint16_t        off = ~colors & ao_led_enable;
+
+       LED_PORT->bsrr = off << 16 | on;
+}
+
+void
+ao_led_toggle(uint16_t colors)
+{
+       LED_PORT->odr ^= (colors & ao_led_enable);
+}
+
+void
+ao_led_for(uint16_t colors, uint16_t ticks) __reentrant
+{
+       ao_led_on(colors);
+       ao_delay(ticks);
+       ao_led_off(colors);
+}
+
+void
+ao_led_init(uint16_t enable)
+{
+       int     bit;
+
+       stm_rcc.ahbenr |= (1 << LED_PORT_ENABLE);
+       ao_led_enable = enable;
+       LED_PORT->odr &= ~enable;
+       for (bit = 0; bit < 16; bit++) {
+               if (enable & (1 << bit)) {
+                       stm_moder_set(LED_PORT, bit, STM_MODER_OUTPUT);
+                       stm_otyper_set(LED_PORT, bit, STM_OTYPER_PUSH_PULL);
+               }
+       }
+}
diff --git a/src/stm/ao_profile.c b/src/stm/ao_profile.c
new file mode 100644 (file)
index 0000000..149ca29
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_profile.h>
+
+static void ao_profile_test(void)
+{
+       uint8_t i;
+       uint32_t        ticks[20];
+
+       for (i = 0; i < 20; i++) {
+               ticks[i] = ao_profile_tick();
+               ao_delay(0);
+       }
+       for (i = 0; i < 19; i++)
+               printf ("%d\n", ticks[i+1] - ticks[i]);
+}
+
+static const struct ao_cmds ao_profile_cmds[] = {
+       { ao_profile_test,      "P\0Test profile counter" },
+       { 0, NULL }
+};
+
+void ao_profile_init(void)
+{
+       /* Turn on timer 2 and 4 */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM2EN) |
+               (1 << STM_RCC_APB1ENR_TIM4EN);
+
+       /* disable timers */
+       stm_tim4.cr1 = 0;
+       stm_tim2.cr1 = 0;
+
+       /* tim4 is master */
+
+
+       stm_tim4.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+                       (STM_TIM234_CR2_MMS_UPDATE << STM_TIM234_CR2_MMS) |
+                       (0 << STM_TIM234_CR2_CCDS));
+
+       stm_tim4.smcr = ((0 << STM_TIM234_SMCR_ETP) |
+                        (0 << STM_TIM234_SMCR_ECE) |
+                        (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
+                        (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
+                        (0 << STM_TIM234_SMCR_MSM) |
+                        (STM_TIM234_SMCR_TS_ITR3 << STM_TIM234_SMCR_TS) |
+                        (0 << STM_TIM234_SMCR_OCCS) |
+                        (STM_TIM234_SMCR_SMS_DISABLE << STM_TIM234_SMCR_SMS));
+
+       stm_tim4.dier = 0;
+       stm_tim4.sr = 0;
+
+       stm_tim4.psc = 31;
+       stm_tim4.cnt = 0;
+       stm_tim4.arr = 0xffff;
+
+       /* tim2 is slaved to tim4 */
+
+       stm_tim2.cr2 = ((0 << STM_TIM234_CR2_TI1S) |
+                       (STM_TIM234_CR2_MMS_ENABLE << STM_TIM234_CR2_MMS) |
+                       (0 << STM_TIM234_CR2_CCDS));
+       stm_tim2.smcr = ((0 << STM_TIM234_SMCR_ETP) |
+                        (0 << STM_TIM234_SMCR_ECE) |
+                        (STM_TIM234_SMCR_ETPS_OFF << STM_TIM234_SMCR_ETPS) |
+                        (STM_TIM234_SMCR_ETF_NONE << STM_TIM234_SMCR_ETF) |
+                        (0 << STM_TIM234_SMCR_MSM) |
+                        (STM_TIM234_SMCR_TS_ITR3 << STM_TIM234_SMCR_TS) |
+                        (0 << STM_TIM234_SMCR_OCCS) |
+                        (STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK << STM_TIM234_SMCR_SMS));
+       stm_tim2.dier = 0;
+       stm_tim2.sr = 0;
+       stm_tim2.psc = 0;
+       stm_tim2.cnt = 0;
+       stm_tim2.arr = 0xffff;
+
+       /* Start your timers */
+
+       stm_tim2.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                       (0 << STM_TIM234_CR1_ARPE) |
+                       (STM_TIM234_CR1_CMS_EDGE | STM_TIM234_CR1_CMS) |
+                       (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+                       (0 << STM_TIM234_CR1_OPM) |
+                       (0 << STM_TIM234_CR1_URS) |
+                       (0 << STM_TIM234_CR1_UDIS) |
+                       (1 << STM_TIM234_CR1_CEN));
+
+       stm_tim4.cr1 = ((STM_TIM234_CR1_CKD_1 << STM_TIM234_CR1_CKD) |
+                       (0 << STM_TIM234_CR1_ARPE) |
+                       (STM_TIM234_CR1_CMS_EDGE | STM_TIM234_CR1_CMS) |
+                       (STM_TIM234_CR1_DIR_UP << STM_TIM234_CR1_DIR) |
+                       (0 << STM_TIM234_CR1_OPM) |
+                       (1 << STM_TIM234_CR1_URS) |
+                       (0 << STM_TIM234_CR1_UDIS) |
+                       (1 << STM_TIM234_CR1_CEN));
+
+       ao_cmd_register(&ao_profile_cmds[0]);
+}
diff --git a/src/stm/ao_profile.h b/src/stm/ao_profile.h
new file mode 100644 (file)
index 0000000..f7dd029
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PROFILE_H_
+#define _AO_PROFILE_H_
+
+void   ao_profile_init();
+
+static uint32_t inline ao_profile_tick(void) {
+       uint16_t        hi, lo, second_hi;
+
+       do {
+               hi = stm_tim2.cnt;
+               lo = stm_tim4.cnt;
+               second_hi = stm_tim2.cnt;
+       } while (hi != second_hi);
+       return ((uint32_t) hi << 16) | lo;
+}
+
+#endif
diff --git a/src/stm/ao_romconfig.c b/src/stm/ao_romconfig.c
new file mode 100644 (file)
index 0000000..cbb922e
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+AO_ROMCONFIG_SYMBOL (0) uint16_t ao_romconfig_version = AO_ROMCONFIG_VERSION;
+AO_ROMCONFIG_SYMBOL (0) uint16_t ao_romconfig_check = ~AO_ROMCONFIG_VERSION;
+AO_ROMCONFIG_SYMBOL (0) uint16_t ao_serial_number = 0;
+#ifdef AO_RADIO_CAL_DEFAULT
+AO_ROMCONFIG_SYMBOL (0) uint32_t ao_radio_cal = AO_RADIO_CAL_DEFAULT;
+#endif
diff --git a/src/stm/ao_serial_stm.c b/src/stm/ao_serial_stm.c
new file mode 100644 (file)
index 0000000..406da9f
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+struct ao_stm_usart {
+       struct ao_fifo          rx_fifo;
+       struct ao_fifo          tx_fifo;
+       struct stm_usart        *reg;
+       uint8_t                 tx_started;
+};
+
+void
+ao_debug_out(char c)
+{
+       if (c == '\n')
+               ao_debug_out('\r');
+       while (!(stm_usart1.sr & (1 << STM_USART_SR_TXE)));
+       stm_usart1.dr = c;
+}
+
+static void
+ao_usart_tx_start(struct ao_stm_usart *usart)
+{
+       if (!ao_fifo_empty(usart->tx_fifo) && !usart->tx_started)
+       {
+               usart->tx_started = 1;
+               ao_fifo_remove(usart->tx_fifo, usart->reg->dr);
+       }
+}
+
+static void
+ao_usart_isr(struct ao_stm_usart *usart, int stdin)
+{
+       uint32_t        sr;
+
+       sr = usart->reg->sr;
+       usart->reg->sr = 0;
+
+       if (sr & (1 << STM_USART_SR_RXNE)) {
+               char c = usart->reg->dr;
+               if (!ao_fifo_full(usart->rx_fifo))
+                       ao_fifo_insert(usart->rx_fifo, c);
+               ao_wakeup(&usart->rx_fifo);
+               if (stdin)
+                       ao_wakeup(&ao_stdin_ready);
+       }
+       if (sr & (1 << STM_USART_SR_TC)) {
+               usart->tx_started = 0;
+               ao_usart_tx_start(usart);
+               ao_wakeup(&usart->tx_fifo);
+       }
+}
+
+char
+ao_usart_getchar(struct ao_stm_usart *usart)
+{
+       char c;
+       cli();
+       while (ao_fifo_empty(usart->rx_fifo))
+               ao_sleep(&usart->rx_fifo);
+       ao_fifo_remove(usart->rx_fifo, c);
+       sei();
+       return c;
+}
+
+char
+ao_usart_pollchar(struct ao_stm_usart *usart)
+{
+       char    c;
+       cli();
+       if (ao_fifo_empty(usart->rx_fifo)) {
+               sei();
+               return AO_READ_AGAIN;
+       }
+       ao_fifo_remove(usart->rx_fifo,c);
+       sei();
+       return c;
+}
+
+void
+ao_usart_putchar(struct ao_stm_usart *usart, char c)
+{
+       cli();
+       while (ao_fifo_full(usart->tx_fifo))
+               ao_sleep(&usart->tx_fifo);
+       ao_fifo_insert(usart->tx_fifo, c);
+       ao_usart_tx_start(usart);
+       sei();
+}
+
+void
+ao_usart_drain(struct ao_stm_usart *usart)
+{
+       cli();
+       while (!ao_fifo_empty(usart->tx_fifo))
+               ao_sleep(&usart->tx_fifo);
+       sei();
+}
+
+static const struct {
+       uint32_t brr;
+} ao_usart_speeds[] = {
+       [AO_SERIAL_SPEED_4800] = {
+               AO_PCLK1 / 4800
+       },
+       [AO_SERIAL_SPEED_9600] = {
+               AO_PCLK1 / 9600
+       },
+       [AO_SERIAL_SPEED_19200] = {
+               AO_PCLK1 / 19200
+       },
+       [AO_SERIAL_SPEED_57600] = {
+               AO_PCLK1 / 57600
+       },
+};
+
+void
+ao_usart_set_speed(struct ao_stm_usart *usart, uint8_t speed)
+{
+       if (speed > AO_SERIAL_SPEED_57600)
+               return;
+       usart->reg->brr = ao_usart_speeds[speed].brr;
+}
+
+void
+ao_usart_init(struct ao_stm_usart *usart)
+{
+       usart->reg->cr1 = ((0 << STM_USART_CR1_OVER8) |
+                         (1 << STM_USART_CR1_UE) |
+                         (0 << STM_USART_CR1_M) |
+                         (0 << STM_USART_CR1_WAKE) |
+                         (0 << STM_USART_CR1_PCE) |
+                         (0 << STM_USART_CR1_PS) |
+                         (0 << STM_USART_CR1_PEIE) |
+                         (0 << STM_USART_CR1_TXEIE) |
+                         (1 << STM_USART_CR1_TCIE) |
+                         (1 << STM_USART_CR1_RXNEIE) |
+                         (0 << STM_USART_CR1_IDLEIE) |
+                         (1 << STM_USART_CR1_TE) |
+                         (1 << STM_USART_CR1_RE) |
+                         (0 << STM_USART_CR1_RWU) |
+                         (0 << STM_USART_CR1_SBK));
+
+       usart->reg->cr2 = ((0 << STM_USART_CR2_LINEN) |
+                         (STM_USART_CR2_STOP_1 << STM_USART_CR2_STOP) |
+                         (0 << STM_USART_CR2_CLKEN) |
+                         (0 << STM_USART_CR2_CPOL) |
+                         (0 << STM_USART_CR2_CPHA) |
+                         (0 << STM_USART_CR2_LBCL) |
+                         (0 << STM_USART_CR2_LBDIE) |
+                         (0 << STM_USART_CR2_LBDL) |
+                         (0 << STM_USART_CR2_ADD));
+
+       usart->reg->cr3 = ((0 << STM_USART_CR3_ONEBITE) |
+                         (0 << STM_USART_CR3_CTSIE) |
+                         (0 << STM_USART_CR3_CTSE) |
+                         (0 << STM_USART_CR3_RTSE) |
+                         (0 << STM_USART_CR3_DMAT) |
+                         (0 << STM_USART_CR3_DMAR) |
+                         (0 << STM_USART_CR3_SCEN) |
+                         (0 << STM_USART_CR3_NACK) |
+                         (0 << STM_USART_CR3_HDSEL) |
+                         (0 << STM_USART_CR3_IRLP) |
+                         (0 << STM_USART_CR3_IREN) |
+                         (0 << STM_USART_CR3_EIE));
+
+       /* Pick a 9600 baud rate */
+       ao_usart_set_speed(usart, AO_SERIAL_SPEED_9600);
+}
+
+#if HAS_SERIAL_1
+
+struct ao_stm_usart ao_stm_usart1;
+
+void stm_usart1_isr(void) { ao_usart_isr(&ao_stm_usart1, USE_SERIAL_1_STDIN); }
+
+char
+ao_serial1_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart1, c);
+}
+
+char
+ao_serial1_pollchar(void)
+{
+       return ao_usart_pollchar(&ao_stm_usart1);
+}
+
+void
+ao_serial1_set_speed(uint8_t speed)
+{
+       ao_usart_set_speed(&ao_stm_usart1, speed);
+}
+#endif /* HAS_SERIAL_1 */
+
+#if HAS_SERIAL_2
+
+struct ao_stm_usart ao_stm_usart2;
+
+void stm_usart2_isr(void) { ao_usart_isr(&ao_stm_usart2, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial2_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart2, c);
+}
+
+char
+ao_serial2_pollchar(void)
+{
+       return ao_usart_pollchar(&ao_stm_usart2);
+}
+
+void
+ao_serial2_set_speed(uint8_t speed)
+{
+       ao_usart_set_speed(&ao_stm_usart2, speed);
+}
+#endif /* HAS_SERIAL_2 */
+
+#if HAS_SERIAL_3
+
+struct ao_stm_usart ao_stm_usart3;
+
+void stm_usart3_isr(void) { ao_usart_isr(&ao_stm_usart3, USE_SERIAL_2_STDIN); }
+
+char
+ao_serial3_getchar(void)
+{
+       return ao_usart_getchar(&ao_stm_usart3);
+}
+
+void
+ao_serial3_putchar(char c)
+{
+       ao_usart_putchar(&ao_stm_usart3, c);
+}
+
+char
+ao_serial3_pollchar(void)
+{
+       return ao_usart_pollchar(&ao_stm_usart3);
+}
+
+void
+ao_serial3_set_speed(uint8_t speed)
+{
+       ao_usart_set_speed(&ao_stm_usart3, speed);
+}
+#endif /* HAS_SERIAL_3 */
+
+void
+ao_serial_init(void)
+{
+#if HAS_SERIAL_1
+       /*
+        *      TX      RX
+        *      PA9     PA10
+        *      PB6     PB7     *
+        */
+
+#if SERIAL_1_PA9_PA10
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+       stm_afr_set(&stm_gpioa, 9, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioa, 10, STM_AFR_AF7);
+#else
+#if SERIAL_1_PB6_PB7
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+       stm_afr_set(&stm_gpiob, 6, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiob, 7, STM_AFR_AF7);
+#else
+#error "No SERIAL_1 port configuration specified"
+#endif
+#endif
+       /* Enable USART */
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_USART1EN);
+
+       ao_stm_usart1.reg = &stm_usart1;
+       ao_usart_init(&ao_stm_usart1);
+
+       stm_nvic_set_enable(STM_ISR_USART1_POS);
+       stm_nvic_set_priority(STM_ISR_USART1_POS, 4);
+#if USE_SERIAL_1_STDIN
+       ao_add_stdio(ao_serial1_pollchar,
+                    ao_serial1_putchar,
+                    NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_2
+       /*
+        *      TX      RX
+        *      PA2     PA3
+        *      PD5     PD6
+        */
+
+#if SERIAL_2_PA2_PA3
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+
+       stm_afr_set(&stm_gpioa, 2, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioa, 3, STM_AFR_AF7);
+#else
+#if SERIAL_2_PD5_PD6
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+       stm_afr_set(&stm_gpiod, 5, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiod, 6, STM_AFR_AF7);
+#else
+#error "No SERIAL_2 port configuration specified"
+#endif 
+#endif
+       /* Enable USART */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART2EN);
+
+       ao_stm_usart2.reg = &stm_usart2;
+       ao_usart_init(&ao_stm_usart2);
+
+       stm_nvic_set_enable(STM_ISR_USART2_POS);
+       stm_nvic_set_priority(STM_ISR_USART2_POS, 4);
+#if USE_SERIAL_2_STDIN
+       ao_add_stdio(ao_serial2_pollchar,
+                    ao_serial2_putchar,
+                    NULL);
+#endif
+#endif
+
+#if HAS_SERIAL_3
+       /*
+        *      TX      RX
+        *      PB10    PB11
+        *      PC10    PC11
+        *      PD8     PD9
+        */
+#if SERIAL_3_PB10_PB11
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+
+       stm_afr_set(&stm_gpiob, 10, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiob, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PC10_PC11
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOCEN);
+
+       stm_afr_set(&stm_gpioc, 10, STM_AFR_AF7);
+       stm_afr_set(&stm_gpioc, 11, STM_AFR_AF7);
+#else
+#if SERIAL_3_PD8_PD9
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+
+       stm_afr_set(&stm_gpiod, 8, STM_AFR_AF7);
+       stm_afr_set(&stm_gpiod, 9, STM_AFR_AF7);
+#else
+#error "No SERIAL_3 port configuration specified"
+#endif
+#endif
+#endif
+       /* Enable USART */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USART3EN);
+
+       ao_stm_usart3.reg = &stm_usart3;
+       ao_usart_init(&ao_stm_usart3);
+
+       stm_nvic_set_enable(STM_ISR_USART3_POS);
+       stm_nvic_set_priority(STM_ISR_USART3_POS, 4);
+#if USE_SERIAL_3_STDIN
+       ao_add_stdio(ao_serial3_pollchar,
+                    ao_serial3_putchar,
+                    NULL);
+#endif
+#endif
+}
+
+
diff --git a/src/stm/ao_spi_stm.c b/src/stm/ao_spi_stm.c
new file mode 100644 (file)
index 0000000..ade86a2
--- /dev/null
@@ -0,0 +1,432 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+
+struct ao_spi_stm_info {
+       uint8_t miso_dma_index;
+       uint8_t mosi_dma_index;
+       struct stm_spi *stm_spi;
+};
+
+static uint8_t         ao_spi_mutex[STM_NUM_SPI];
+static uint8_t         ao_spi_config[STM_NUM_SPI];
+
+static const struct ao_spi_stm_info ao_spi_stm_info[STM_NUM_SPI] = {
+       {
+               .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_RX),
+               .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI1_TX),
+               &stm_spi1
+       },
+       {
+               .miso_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_RX),
+               .mosi_dma_index = STM_DMA_INDEX(STM_DMA_CHANNEL_SPI2_TX),
+               &stm_spi2
+       }
+};
+
+static uint8_t spi_dev_null;
+
+void
+ao_spi_send(void *block, uint16_t len, uint8_t spi_index)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
+       uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
+
+       /* Set up the transmit DMA to deliver data */
+       ao_dma_set_transfer(mosi_dma_index,
+                           &stm_spi->dr,
+                           block,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+       /* Clear RXNE */
+       (void) stm_spi->dr;
+
+       /* Set up the receive DMA -- when this is done, we know the SPI unit
+        * is idle. Without this, we'd have to poll waiting for the BSY bit to
+        * be cleared
+        */
+       ao_dma_set_transfer(miso_dma_index,
+                           &stm_spi->dr,
+                           &spi_dev_null,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (0 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (1 << STM_SPI_CR2_TXDMAEN) |
+                       (1 << STM_SPI_CR2_RXDMAEN));
+       ao_dma_start(miso_dma_index);
+       ao_dma_start(mosi_dma_index);
+       ao_arch_critical(
+               while (!ao_dma_done[miso_dma_index])
+                       ao_sleep(&ao_dma_done[miso_dma_index]);
+               );
+       ao_dma_done_transfer(mosi_dma_index);
+       ao_dma_done_transfer(miso_dma_index);
+}
+
+void
+ao_spi_send_fixed(uint8_t value, uint16_t len, uint8_t spi_index)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
+       uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
+
+       /* Set up the transmit DMA to deliver data */
+       ao_dma_set_transfer(mosi_dma_index,
+                           &stm_spi->dr,
+                           &value,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (0 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+       /* Clear RXNE */
+       (void) stm_spi->dr;
+
+       /* Set up the receive DMA -- when this is done, we know the SPI unit
+        * is idle. Without this, we'd have to poll waiting for the BSY bit to
+        * be cleared
+        */
+       ao_dma_set_transfer(miso_dma_index,
+                           &stm_spi->dr,
+                           &spi_dev_null,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (0 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (1 << STM_SPI_CR2_TXDMAEN) |
+                       (1 << STM_SPI_CR2_RXDMAEN));
+       ao_dma_start(miso_dma_index);
+       ao_dma_start(mosi_dma_index);
+       ao_arch_critical(
+               while (!ao_dma_done[miso_dma_index])
+                       ao_sleep(&ao_dma_done[miso_dma_index]);
+               );
+       ao_dma_done_transfer(mosi_dma_index);
+       ao_dma_done_transfer(miso_dma_index);
+}
+
+void
+ao_spi_recv(void *block, uint16_t len, uint8_t spi_index)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
+       uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
+
+       /* Set up transmit DMA to make the SPI hardware actually run */
+       ao_dma_set_transfer(mosi_dma_index,
+                           &stm_spi->dr,
+                           &spi_dev_null,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (0 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+       /* Clear RXNE */
+       (void) stm_spi->dr;
+
+       /* Set up the receive DMA to capture data */
+       ao_dma_set_transfer(miso_dma_index,
+                           &stm_spi->dr,
+                           block,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (1 << STM_SPI_CR2_TXDMAEN) |
+                       (1 << STM_SPI_CR2_RXDMAEN));
+       ao_dma_start(miso_dma_index);
+       ao_dma_start(mosi_dma_index);
+
+       /* Wait until the SPI unit is done */
+       ao_arch_critical(
+               while (!ao_dma_done[miso_dma_index])
+                       ao_sleep(&ao_dma_done[miso_dma_index]);
+               );
+
+       ao_dma_done_transfer(mosi_dma_index);
+       ao_dma_done_transfer(miso_dma_index);
+}
+
+void
+ao_spi_duplex(void *out, void *in, uint16_t len, uint8_t spi_index)
+{
+       struct stm_spi *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
+       uint8_t mosi_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].mosi_dma_index;
+       uint8_t miso_dma_index = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].miso_dma_index;
+
+       /* Set up transmit DMA to send data */
+       ao_dma_set_transfer(mosi_dma_index,
+                           &stm_spi->dr,
+                           out,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
+
+       /* Clear RXNE */
+       (void) stm_spi->dr;
+
+       /* Set up the receive DMA to capture data */
+       ao_dma_set_transfer(miso_dma_index,
+                           &stm_spi->dr,
+                           in,
+                           len,
+                           (0 << STM_DMA_CCR_MEM2MEM) |
+                           (STM_DMA_CCR_PL_MEDIUM << STM_DMA_CCR_PL) |
+                           (STM_DMA_CCR_MSIZE_8 << STM_DMA_CCR_MSIZE) |
+                           (STM_DMA_CCR_PSIZE_8 << STM_DMA_CCR_PSIZE) |
+                           (1 << STM_DMA_CCR_MINC) |
+                           (0 << STM_DMA_CCR_PINC) |
+                           (0 << STM_DMA_CCR_CIRC) |
+                           (STM_DMA_CCR_DIR_PER_TO_MEM << STM_DMA_CCR_DIR));
+
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (1 << STM_SPI_CR2_TXDMAEN) |
+                       (1 << STM_SPI_CR2_RXDMAEN));
+       ao_dma_start(miso_dma_index);
+       ao_dma_start(mosi_dma_index);
+
+       /* Wait until the SPI unit is done */
+       ao_arch_critical(
+               while (!ao_dma_done[miso_dma_index])
+                       ao_sleep(&ao_dma_done[miso_dma_index]);
+               );
+
+       ao_dma_done_transfer(mosi_dma_index);
+       ao_dma_done_transfer(miso_dma_index);
+}
+
+void
+ao_spi_get(uint8_t spi_index, uint32_t speed)
+{
+       struct stm_spi  *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
+       uint8_t         config = AO_SPI_CONFIG(spi_index);
+
+       ao_mutex_get(&ao_spi_mutex[AO_SPI_INDEX(spi_index)]);
+       if (config != ao_spi_config[AO_SPI_INDEX(spi_index)]) {
+               
+               /* Disable current config
+                */
+               switch (AO_SPI_INDEX(spi_index)) {
+               case STM_SPI_INDEX(1):
+                       switch (ao_spi_config[AO_SPI_INDEX(spi_index)]) {
+                       case AO_SPI_1_CONFIG_PA5_PA6_PA7:
+                               stm_gpio_set(&stm_gpioa, 5, 0);
+                               stm_moder_set(&stm_gpioa, 5, STM_MODER_OUTPUT);
+                               stm_moder_set(&stm_gpioa, 6, STM_MODER_INPUT);
+                               stm_moder_set(&stm_gpioa, 7, STM_MODER_OUTPUT);
+                               break;
+                       case AO_SPI_1_CONFIG_PB3_PB4_PB5:
+                               stm_gpio_set(&stm_gpiob, 3, 0);
+                               stm_moder_set(&stm_gpiob, 3, STM_MODER_OUTPUT);
+                               stm_moder_set(&stm_gpiob, 4, STM_MODER_INPUT);
+                               stm_moder_set(&stm_gpiob, 5, STM_MODER_OUTPUT);
+                               break;
+                       case AO_SPI_1_CONFIG_PE13_PE14_PE15:
+                               stm_gpio_set(&stm_gpioe, 13, 0);
+                               stm_moder_set(&stm_gpioe, 13, STM_MODER_OUTPUT);
+                               stm_moder_set(&stm_gpioe, 14, STM_MODER_INPUT);
+                               stm_moder_set(&stm_gpioe, 15, STM_MODER_OUTPUT);
+                               break;
+                       }
+                       break;
+               case STM_SPI_INDEX(2):
+                       switch (ao_spi_config[AO_SPI_INDEX(spi_index)]) {
+                       case AO_SPI_2_CONFIG_PB13_PB14_PB15:
+                               stm_gpio_set(&stm_gpiob, 13, 0);
+                               stm_moder_set(&stm_gpiob, 13, STM_MODER_OUTPUT);
+                               stm_moder_set(&stm_gpiob, 14, STM_MODER_INPUT);
+                               stm_moder_set(&stm_gpiob, 15, STM_MODER_OUTPUT);
+                               break;
+                       case AO_SPI_2_CONFIG_PD1_PD3_PD4:
+                               stm_gpio_set(&stm_gpiod, 1, 0);
+                               stm_moder_set(&stm_gpiod, 1, STM_MODER_OUTPUT);
+                               stm_moder_set(&stm_gpiod, 3, STM_MODER_INPUT);
+                               stm_moder_set(&stm_gpiod, 4, STM_MODER_OUTPUT);
+                               break;
+                       }
+                       break;
+               }
+
+               /* Enable new config
+                */
+               switch (AO_SPI_INDEX(spi_index)) {
+               case 0:
+                       switch (AO_SPI_CONFIG(spi_index)) {
+                       case AO_SPI_1_CONFIG_PA5_PA6_PA7:
+                               stm_afr_set(&stm_gpioa, 5, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpioa, 6, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpioa, 7, STM_AFR_AF5);
+                               break;
+                       case AO_SPI_1_CONFIG_PB3_PB4_PB5:
+                               stm_afr_set(&stm_gpiob, 3, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpiob, 4, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpiob, 5, STM_AFR_AF5);
+                               break;
+                       case AO_SPI_1_CONFIG_PE13_PE14_PE15:
+                               stm_afr_set(&stm_gpioe, 13, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpioe, 14, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpioe, 15, STM_AFR_AF5);
+                               break;
+                       }
+                       break;
+               case 1:
+                       switch (AO_SPI_CONFIG(spi_index)) {
+                       case AO_SPI_2_CONFIG_PB13_PB14_PB15:
+                               stm_afr_set(&stm_gpiob, 13, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpiob, 14, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpiob, 15, STM_AFR_AF5);
+                               break;
+                       case AO_SPI_2_CONFIG_PD1_PD3_PD4:
+                               stm_afr_set(&stm_gpiod, 1, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpiod, 3, STM_AFR_AF5);
+                               stm_afr_set(&stm_gpiod, 4, STM_AFR_AF5);
+                               break;
+                       }
+                       break;
+               }
+               ao_spi_config[AO_SPI_INDEX(spi_index)] = AO_SPI_CONFIG(spi_index);
+       }
+       stm_spi->cr1 = ((0 << STM_SPI_CR1_BIDIMODE) |                   /* Three wire mode */
+                       (0 << STM_SPI_CR1_BIDIOE) |
+                       (0 << STM_SPI_CR1_CRCEN) |                      /* CRC disabled */
+                       (0 << STM_SPI_CR1_CRCNEXT) |
+                       (0 << STM_SPI_CR1_DFF) |
+                       (0 << STM_SPI_CR1_RXONLY) |
+                       (1 << STM_SPI_CR1_SSM) |                        /* Software SS handling */
+                       (1 << STM_SPI_CR1_SSI) |                        /*  ... */
+                       (0 << STM_SPI_CR1_LSBFIRST) |                   /* Big endian */
+                       (1 << STM_SPI_CR1_SPE) |                        /* Enable SPI unit */
+                       (speed << STM_SPI_CR1_BR) |     /* baud rate to pclk/4 */
+                       (1 << STM_SPI_CR1_MSTR) |
+                       (0 << STM_SPI_CR1_CPOL) |                       /* Format 0 */
+                       (0 << STM_SPI_CR1_CPHA));
+}
+
+void
+ao_spi_put(uint8_t spi_index)
+{
+       struct stm_spi  *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
+
+       stm_spi->cr1 = 0;
+       ao_mutex_put(&ao_spi_mutex[AO_SPI_INDEX(spi_index)]);
+}
+
+static void
+ao_spi_channel_init(uint8_t spi_index)
+{
+       struct stm_spi  *stm_spi = ao_spi_stm_info[AO_SPI_INDEX(spi_index)].stm_spi;
+
+       stm_spi->cr1 = 0;
+       (void) stm_spi->sr;
+       stm_spi->cr2 = ((0 << STM_SPI_CR2_TXEIE) |
+                       (0 << STM_SPI_CR2_RXNEIE) |
+                       (0 << STM_SPI_CR2_ERRIE) |
+                       (0 << STM_SPI_CR2_SSOE) |
+                       (0 << STM_SPI_CR2_TXDMAEN) |
+                       (0 << STM_SPI_CR2_RXDMAEN));
+}
+
+void
+ao_spi_init(void)
+{
+#if HAS_SPI_1
+# if SPI_1_PA5_PA6_PA7
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+# endif
+# if SPI_1_PB3_PB4_PB5
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+# endif
+# if SPI_1_PE13_PE14_PE15
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOEEN);
+# endif
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SPI1EN);
+       ao_spi_config[0] = AO_SPI_CONFIG_NONE;
+       ao_spi_channel_init(0);
+#endif
+
+#if HAS_SPI_2
+# if SPI_2_PB13_PB14_PB15
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOBEN);
+# endif
+# if SPI_2_PD1_PD3_PD4
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIODEN);
+# endif
+
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_SPI2EN);
+       ao_spi_config[1] = AO_SPI_CONFIG_NONE;
+
+       ao_spi_channel_init(1);
+#endif
+}
diff --git a/src/stm/ao_timer.c b/src/stm/ao_timer.c
new file mode 100644 (file)
index 0000000..1132f74
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+volatile __data AO_TICK_TYPE ao_tick_count;
+
+uint16_t ao_time(void)
+{
+       uint16_t        v;
+       ao_arch_critical(
+               v = ao_tick_count;
+               );
+       return v;
+}
+
+#if AO_DATA_ALL
+volatile __data uint8_t        ao_data_interval = 1;
+volatile __data uint8_t        ao_data_count;
+#endif
+
+void
+ao_debug_out(char c);
+
+
+void stm_tim6_isr(void)
+{
+       if (stm_tim6.sr & (1 << STM_TIM67_SR_UIF)) {
+               stm_tim6.sr = 0;
+               ++ao_tick_count;
+#if AO_DATA_ALL
+               if (++ao_data_count == ao_data_interval) {
+                       ao_data_count = 0;
+                       ao_adc_poll();
+#if (AO_DATA_ALL & ~(AO_DATA_ADC))
+                       ao_wakeup((void *) &ao_data_count);
+#endif
+               }
+#endif
+       }
+}
+
+#if HAS_ADC
+void
+ao_timer_set_adc_interval(uint8_t interval) __critical
+{
+       ao_data_interval = interval;
+       ao_data_count = 0;
+}
+#endif
+
+/*
+ * According to the STM clock-configuration, timers run
+ * twice as fast as the APB1 clock *if* the APB1 prescaler
+ * is greater than 1.
+ */
+
+#if AO_APB1_PRESCALER > 1
+#define TIMER_23467_SCALER 2
+#else
+#define TIMER_23467_SCALER 1
+#endif
+
+#define TIMER_10kHz    ((AO_PCLK1 * TIMER_23467_SCALER) / 10000)
+
+void
+ao_timer_init(void)
+{
+       stm_nvic_set_enable(STM_ISR_TIM6_POS);
+       stm_nvic_set_priority(STM_ISR_TIM6_POS, AO_STM_NVIC_CLOCK_PRIORITY);
+
+       /* Turn on timer 6 */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_TIM6EN);
+
+       stm_tim6.psc = TIMER_10kHz;
+       stm_tim6.arr = 99;
+       stm_tim6.cnt = 0;
+
+       /* Enable update interrupt */
+       stm_tim6.dier = (1 << STM_TIM67_DIER_UIE);
+
+       /* Poke timer to reload values */
+       stm_tim6.egr |= (1 << STM_TIM67_EGR_UG);
+
+       stm_tim6.cr2 = (STM_TIM67_CR2_MMS_RESET << STM_TIM67_CR2_MMS);
+
+       /* And turn it on */
+       stm_tim6.cr1 = ((0 << STM_TIM67_CR1_ARPE) |
+                       (0 << STM_TIM67_CR1_OPM) |
+                       (1 << STM_TIM67_CR1_URS) |
+                       (0 << STM_TIM67_CR1_UDIS) |
+                       (1 << STM_TIM67_CR1_CEN));
+}
+
+void
+ao_clock_init(void)
+{
+       uint32_t        cfgr;
+       uint32_t        cr;
+       
+       /* Switch to MSI while messing about */
+       stm_rcc.cr |= (1 << STM_RCC_CR_MSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_MSIRDY)))
+               asm("nop");
+
+       /* reset SW, HPRE, PPRE1, PPRE2, MCOSEL and MCOPRE */
+       stm_rcc.cfgr &= (uint32_t)0x88FFC00C;
+
+       /* reset HSION, HSEON, CSSON and PLLON bits */
+       stm_rcc.cr &= 0xeefefffe;
+       
+       /* reset PLLSRC, PLLMUL and PLLDIV bits */
+       stm_rcc.cfgr &= 0xff02ffff;
+       
+       /* Disable all interrupts */
+       stm_rcc.cir = 0;
+
+#if AO_HSE
+#if AO_HSE_BYPASS
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEBYP);
+#else
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_HSEBYP);
+#endif
+       /* Enable HSE clock */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSEON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSERDY)))
+               asm("nop");
+
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSE << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSE)
+#define STM_PLLSRC                             AO_HSE
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (1 << STM_RCC_CFGR_PLLSRC)
+#else
+#define STM_HSI                                16000000
+#define STM_RCC_CFGR_SWS_TARGET_CLOCK          (STM_RCC_CFGR_SWS_HSI << STM_RCC_CFGR_SWS)
+#define STM_RCC_CFGR_SW_TARGET_CLOCK           (STM_RCC_CFGR_SW_HSI)
+#define STM_PLLSRC                             STM_HSI
+#define STM_RCC_CFGR_PLLSRC_TARGET_CLOCK       (0 << STM_RCC_CFGR_PLLSRC)
+#endif
+
+#if !AO_HSE || HAS_ADC
+       /* Enable HSI RC clock 16MHz */
+       stm_rcc.cr |= (1 << STM_RCC_CR_HSION);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_HSIRDY)))
+               asm("nop");
+#endif
+
+       /* Set flash latency to tolerate 32MHz SYSCLK  -> 1 wait state */
+
+       /* Enable 64-bit access and prefetch */
+       stm_flash.acr |= (1 << STM_FLASH_ACR_ACC64);
+       stm_flash.acr |= (1 << STM_FLASH_ACR_PRFEN);
+
+       /* Enable 1 wait state so the CPU can run at 32MHz */
+       /* (haven't managed to run the CPU at 32MHz yet, it's at 16MHz) */
+       stm_flash.acr |= (1 << STM_FLASH_ACR_LATENCY);
+
+       /* Enable power interface clock */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_PWREN);
+
+       /* Set voltage range to 1.8V */
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+
+       /* Configure voltage scaling range */
+       cr = stm_pwr.cr;
+       cr &= ~(STM_PWR_CR_VOS_MASK << STM_PWR_CR_VOS);
+       cr |= (STM_PWR_CR_VOS_1_8 << STM_PWR_CR_VOS);
+       stm_pwr.cr = cr;
+
+       /* poll VOSF bit in PWR_CSR. Wait until it is reset to 0 */
+       while ((stm_pwr.csr & (1 << STM_PWR_CSR_VOSF)) != 0)
+               asm("nop");
+
+       /* HCLK to 16MHz -> AHB prescaler = /1 */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE);
+       cfgr |= (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE);
+       stm_rcc.cfgr = cfgr;
+       while ((stm_rcc.cfgr & (STM_RCC_CFGR_HPRE_MASK << STM_RCC_CFGR_HPRE)) !=
+              (AO_RCC_CFGR_HPRE_DIV << STM_RCC_CFGR_HPRE))
+               asm ("nop");
+
+       /* APB1 Prescaler = AO_APB1_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE1_MASK << STM_RCC_CFGR_PPRE1);
+       cfgr |= (AO_RCC_CFGR_PPRE1_DIV << STM_RCC_CFGR_PPRE1);
+       stm_rcc.cfgr = cfgr;
+
+       /* APB2 Prescaler = AO_APB2_PRESCALER */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PPRE2_MASK << STM_RCC_CFGR_PPRE2);
+       cfgr |= (AO_RCC_CFGR_PPRE2_DIV << STM_RCC_CFGR_PPRE2);
+       stm_rcc.cfgr = cfgr;
+
+       /* Disable the PLL */
+       stm_rcc.cr &= ~(1 << STM_RCC_CR_PLLON);
+       while (stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY))
+               asm("nop");
+       
+       /* PLLVCO to 96MHz (for USB) -> PLLMUL = 6, PLLDIV = 4 */
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_PLLMUL_MASK << STM_RCC_CFGR_PLLMUL);
+       cfgr &= ~(STM_RCC_CFGR_PLLDIV_MASK << STM_RCC_CFGR_PLLDIV);
+
+       cfgr |= (AO_RCC_CFGR_PLLMUL << STM_RCC_CFGR_PLLMUL);
+       cfgr |= (AO_RCC_CFGR_PLLDIV << STM_RCC_CFGR_PLLDIV);
+
+       /* PLL source */
+       cfgr &= ~(1 << STM_RCC_CFGR_PLLSRC);
+       cfgr |= STM_RCC_CFGR_PLLSRC_TARGET_CLOCK;
+
+       stm_rcc.cfgr = cfgr;
+
+       /* Enable the PLL and wait for it */
+       stm_rcc.cr |= (1 << STM_RCC_CR_PLLON);
+       while (!(stm_rcc.cr & (1 << STM_RCC_CR_PLLRDY)))
+               asm("nop");
+
+       /* Switch to the PLL for the system clock */
+
+       cfgr = stm_rcc.cfgr;
+       cfgr &= ~(STM_RCC_CFGR_SW_MASK << STM_RCC_CFGR_SW);
+       cfgr |= (STM_RCC_CFGR_SW_PLL << STM_RCC_CFGR_SW);
+       stm_rcc.cfgr = cfgr;
+       for (;;) {
+               uint32_t        c, part, mask, val;
+
+               c = stm_rcc.cfgr;
+               mask = (STM_RCC_CFGR_SWS_MASK << STM_RCC_CFGR_SWS);
+               val = (STM_RCC_CFGR_SWS_PLL << STM_RCC_CFGR_SWS);
+               part = c & mask;
+               if (part == val)
+                       break;
+       }
+
+#if 0
+       stm_rcc.apb2rstr = 0xffff;
+       stm_rcc.apb1rstr = 0xffff;
+       stm_rcc.ahbrstr = 0x3f;
+       stm_rcc.ahbenr = (1 << STM_RCC_AHBENR_FLITFEN);
+       stm_rcc.apb2enr = 0;
+       stm_rcc.apb1enr = 0;
+       stm_rcc.ahbrstr = 0;
+       stm_rcc.apb1rstr = 0;
+       stm_rcc.apb2rstr = 0;
+#endif
+
+       /* Clear reset flags */
+       stm_rcc.csr |= (1 << STM_RCC_CSR_RMVF);
+
+
+       /* Output SYSCLK on PA8 for measurments */
+
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_GPIOAEN);
+       
+       stm_afr_set(&stm_gpioa, 8, STM_AFR_AF0);
+       stm_moder_set(&stm_gpioa, 8, STM_MODER_ALTERNATE);
+       stm_ospeedr_set(&stm_gpioa, 8, STM_OSPEEDR_40MHz);
+
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOPRE_DIV_1 << STM_RCC_CFGR_MCOPRE);
+       stm_rcc.cfgr |= (STM_RCC_CFGR_MCOSEL_HSE << STM_RCC_CFGR_MCOSEL);
+}
diff --git a/src/stm/ao_usb_stm.c b/src/stm/ao_usb_stm.c
new file mode 100644 (file)
index 0000000..4f37a7d
--- /dev/null
@@ -0,0 +1,1040 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_usb.h"
+#include "ao_product.h"
+
+#define USB_DEBUG      0
+#define USB_DEBUG_DATA 0
+#define USB_ECHO       0
+
+#if USB_DEBUG
+#define debug(format, args...) printf(format, ## args);
+#else
+#define debug(format, args...)
+#endif
+
+#if USB_DEBUG_DATA
+#define debug_data(format, args...)    printf(format, ## args);
+#else
+#define debug_data(format, args...)
+#endif
+
+struct ao_task ao_usb_task;
+
+struct ao_usb_setup {
+       uint8_t         dir_type_recip;
+       uint8_t         request;
+       uint16_t        value;
+       uint16_t        index;
+       uint16_t        length;
+} ao_usb_setup;
+
+static uint8_t         ao_usb_ep0_state;
+
+/* Pending EP0 IN data */
+static const uint8_t   *ao_usb_ep0_in_data;    /* Remaining data */
+static uint8_t                 ao_usb_ep0_in_len;      /* Remaining amount */
+
+/* Temp buffer for smaller EP0 in data */
+static uint8_t ao_usb_ep0_in_buf[2];
+
+/* Pending EP0 OUT data */
+static uint8_t *ao_usb_ep0_out_data;
+static uint8_t         ao_usb_ep0_out_len;
+
+/*
+ * Objects allocated in special USB memory
+ */
+
+/* Buffer description tables */
+static union stm_usb_bdt       *ao_usb_bdt;
+/* USB address of end of allocated storage */
+static uint16_t        ao_usb_sram_addr;
+
+/* Pointer to ep0 tx/rx buffers in USB memory */
+static uint32_t        *ao_usb_ep0_tx_buffer;
+static uint32_t        *ao_usb_ep0_rx_buffer;
+
+/* Pointer to bulk data tx/rx buffers in USB memory */
+static uint32_t        *ao_usb_in_tx_buffer;
+static uint32_t        *ao_usb_out_rx_buffer;
+
+/* System ram shadow of USB buffer; writing individual bytes is
+ * too much of a pain (sigh) */
+static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE];
+static uint8_t ao_usb_tx_count;
+
+static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE];
+static uint8_t ao_usb_rx_count, ao_usb_rx_pos;
+
+/*
+ * End point register indices
+ */
+
+#define AO_USB_CONTROL_EPR     0
+#define AO_USB_INT_EPR         1
+#define AO_USB_OUT_EPR         2
+#define AO_USB_IN_EPR          3
+
+/* Marks when we don't need to send an IN packet.
+ * This happens only when the last IN packet is not full,
+ * otherwise the host will expect to keep seeing packets.
+ * Send a zero-length packet as required
+ */
+static uint8_t ao_usb_in_flushed;
+
+/* Marks when we have delivered an IN packet to the hardware
+ * and it has not been received yet. ao_sleep on this address
+ * to wait for it to be delivered.
+ */
+static uint8_t ao_usb_in_pending;
+
+/* Marks when an OUT packet has been received by the hardware
+ * but not pulled to the shadow buffer.
+ */
+static uint8_t ao_usb_out_avail;
+static uint8_t ao_usb_running;
+static uint8_t ao_usb_configuration;
+static uint8_t ueienx_0;
+
+#define AO_USB_EP0_GOT_RESET   1
+#define AO_USB_EP0_GOT_SETUP   2
+#define AO_USB_EP0_GOT_RX_DATA 4
+#define AO_USB_EP0_GOT_TX_ACK  8
+
+static uint8_t ao_usb_ep0_receive;
+static uint8_t ao_usb_address;
+static uint8_t ao_usb_address_pending;
+
+static inline uint32_t set_toggle(uint32_t     current_value,
+                                  uint32_t     mask,
+                                  uint32_t     desired_value)
+{
+       return (current_value ^ desired_value) & mask;
+}
+
+static inline uint32_t *ao_usb_packet_buffer_addr(uint16_t sram_addr)
+{
+       return (uint32_t *) (stm_usb_sram + 2 * sram_addr);
+}
+
+static inline uint32_t ao_usb_epr_stat_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_STAT_RX) & STM_USB_EPR_STAT_RX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_stat_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_STAT_TX) & STM_USB_EPR_STAT_TX_MASK;
+}
+
+static inline uint32_t ao_usb_epr_ctr_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_CTR_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_ctr_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_CTR_TX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_setup(uint32_t epr) {
+       return (epr >> STM_USB_EPR_SETUP) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_rx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_DTOG_RX) & 1;
+}
+
+static inline uint32_t ao_usb_epr_dtog_tx(uint32_t epr) {
+       return (epr >> STM_USB_EPR_DTOG_TX) & 1;
+}
+
+/*
+ * Set current device address and mark the
+ * interface as active
+ */
+void
+ao_usb_set_address(uint8_t address)
+{
+       debug("ao_usb_set_address %02x\n", address);
+       stm_usb.daddr = (1 << STM_USB_DADDR_EF) | address;
+       ao_usb_address_pending = 0;
+}
+
+/*
+ * Write these values to preserve register contents under HW changes
+ */
+
+#define STM_USB_EPR_INVARIANT  ((1 << STM_USB_EPR_CTR_RX) |            \
+                                (STM_USB_EPR_DTOG_RX_WRITE_INVARIANT << STM_USB_EPR_DTOG_RX) | \
+                                (STM_USB_EPR_STAT_RX_WRITE_INVARIANT << STM_USB_EPR_STAT_RX) | \
+                                (1 << STM_USB_EPR_CTR_TX) |            \
+                                (STM_USB_EPR_DTOG_TX_WRITE_INVARIANT << STM_USB_EPR_DTOG_TX) | \
+                                (STM_USB_EPR_STAT_TX_WRITE_INVARIANT << STM_USB_EPR_STAT_TX))
+
+#define STM_USB_EPR_INVARIANT_MASK     ((1 << STM_USB_EPR_CTR_RX) |    \
+                                        (STM_USB_EPR_DTOG_RX_MASK << STM_USB_EPR_DTOG_RX) | \
+                                        (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX) | \
+                                        (1 << STM_USB_EPR_CTR_TX) |    \
+                                        (STM_USB_EPR_DTOG_TX_MASK << STM_USB_EPR_DTOG_TX) | \
+                                        (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX))
+
+/*
+ * These bits are purely under sw control, so preserve them in the
+ * register by re-writing what was read
+ */
+#define STM_USB_EPR_PRESERVE_MASK      ((STM_USB_EPR_EP_TYPE_MASK << STM_USB_EPR_EP_TYPE) | \
+                                        (1 << STM_USB_EPR_EP_KIND) |   \
+                                        (STM_USB_EPR_EA_MASK << STM_USB_EPR_EA))
+
+/*
+ * Set the state of the specified endpoint register to a new
+ * value. This is tricky because the bits toggle where the new
+ * value is one, and we need to write invariant values in other
+ * spots of the register. This hardware is strange...
+ */
+static void
+_ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+       uint32_t        epr_write, epr_old;
+
+       epr_old = epr_write = stm_usb.epr[ep];
+       epr_write &= STM_USB_EPR_PRESERVE_MASK;
+       epr_write |= STM_USB_EPR_INVARIANT;
+       epr_write |= set_toggle(epr_old,
+                             STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX,
+                             stat_tx << STM_USB_EPR_STAT_TX);
+       stm_usb.epr[ep] = epr_write;
+}
+
+static void
+ao_usb_set_stat_tx(int ep, uint32_t stat_tx)
+{
+       cli();
+       _ao_usb_set_stat_tx(ep, stat_tx);
+       sei();
+}
+
+static void
+ao_usb_set_stat_rx(int ep, uint32_t stat_rx) {
+       uint32_t        epr_write, epr_old;
+
+       cli();
+       epr_write = epr_old = stm_usb.epr[ep];
+       epr_write &= STM_USB_EPR_PRESERVE_MASK;
+       epr_write |= STM_USB_EPR_INVARIANT;
+       epr_write |= set_toggle(epr_old,
+                             STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX,
+                             stat_rx << STM_USB_EPR_STAT_RX);
+       stm_usb.epr[ep] = epr_write;
+       sei();
+}
+
+/*
+ * Set just endpoint 0, for use during startup
+ */
+
+static void
+ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint32_t stat_tx)
+{
+       uint32_t                epr;
+       cli();
+       epr = stm_usb.epr[ep];
+       epr = ((0 << STM_USB_EPR_CTR_RX) |
+              (epr & (1 << STM_USB_EPR_DTOG_RX)) |
+              set_toggle(epr,
+                         (STM_USB_EPR_STAT_RX_MASK << STM_USB_EPR_STAT_RX),
+                         (stat_rx << STM_USB_EPR_STAT_RX)) |
+              (type << STM_USB_EPR_EP_TYPE) |
+              (0 << STM_USB_EPR_EP_KIND) |
+              (0 << STM_USB_EPR_CTR_TX) |
+              (epr & (1 << STM_USB_EPR_DTOG_TX)) |
+              set_toggle(epr,
+                         (STM_USB_EPR_STAT_TX_MASK << STM_USB_EPR_STAT_TX),
+                         (stat_tx << STM_USB_EPR_STAT_TX)) |
+              (addr << STM_USB_EPR_EA));
+       stm_usb.epr[ep] = epr;
+       sei();
+       debug ("writing epr[%d] 0x%08x wrote 0x%08x\n",
+              ep, epr, stm_usb.epr[ep]);
+}
+
+static void
+ao_usb_set_ep0(void)
+{
+       uint32_t                epr;
+       int                     e;
+
+       ao_usb_sram_addr = 0;
+
+       /* buffer table is at the start of USB memory */
+       stm_usb.btable = 0;
+       ao_usb_bdt = (void *) stm_usb_sram;
+
+       ao_usb_sram_addr += 8 * STM_USB_BDT_SIZE;
+
+       /* Set up EP 0 - a Control end point with 32 bytes of in and out buffers */
+
+       ao_usb_bdt[0].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[0].single.count_tx = 0;
+       ao_usb_ep0_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+       ao_usb_bdt[0].single.addr_rx = ao_usb_sram_addr;
+       ao_usb_bdt[0].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+                                 (((AO_USB_CONTROL_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+       ao_usb_ep0_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_CONTROL_SIZE;
+
+       ao_usb_init_ep(AO_USB_CONTROL_EPR, AO_USB_CONTROL_EP,
+                      STM_USB_EPR_EP_TYPE_CONTROL,
+                      STM_USB_EPR_STAT_RX_VALID,
+                      STM_USB_EPR_STAT_TX_NAK);
+
+       /* Clear all of the other endpoints */
+       for (e = 1; e < 8; e++) {
+               ao_usb_init_ep(e, 0,
+                              STM_USB_EPR_EP_TYPE_CONTROL,
+                              STM_USB_EPR_STAT_RX_DISABLED,
+                              STM_USB_EPR_STAT_TX_DISABLED);
+       }
+
+       ao_usb_set_address(0);
+}
+
+static void
+ao_usb_set_configuration(void)
+{
+       uint32_t                epr;
+
+       debug ("ao_usb_set_configuration\n");
+
+       /* Set up the INT end point */
+       ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
+       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+       ao_usb_init_ep(AO_USB_INT_EPR,
+                      AO_USB_INT_EP,
+                      STM_USB_EPR_EP_TYPE_INTERRUPT,
+                      STM_USB_EPR_STAT_RX_DISABLED,
+                      STM_USB_EPR_STAT_TX_NAK);
+       
+       /* Set up the OUT end point */
+       ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) |
+                                                     (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK));
+       ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+       ao_usb_init_ep(AO_USB_OUT_EPR,
+                      AO_USB_OUT_EP,
+                      STM_USB_EPR_EP_TYPE_BULK,
+                      STM_USB_EPR_STAT_RX_VALID,
+                      STM_USB_EPR_STAT_TX_DISABLED);
+       
+       /* Set up the IN end point */
+       ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr;
+       ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
+       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_sram_addr += AO_USB_IN_SIZE;
+
+       ao_usb_init_ep(AO_USB_IN_EPR,
+                      AO_USB_IN_EP,
+                      STM_USB_EPR_EP_TYPE_BULK,
+                      STM_USB_EPR_STAT_RX_DISABLED,
+                      STM_USB_EPR_STAT_TX_NAK);
+
+       ao_usb_running = 1;
+}
+
+static uint16_t        control_count;
+static uint16_t int_count;
+static uint16_t        in_count;
+static uint16_t        out_count;
+static uint16_t        reset_count;
+
+void
+stm_usb_lp_isr(void)
+{
+       uint32_t        istr = stm_usb.istr;
+
+       if (istr & (1 << STM_USB_ISTR_CTR)) {
+               uint8_t         ep = istr & STM_USB_ISTR_EP_ID_MASK;
+               uint32_t        epr, epr_write;
+
+               /* Preserve the SW write bits, don't mess with most HW writable bits,
+                * clear the CTR_RX and CTR_TX bits
+                */
+               epr = stm_usb.epr[ep];
+               epr_write = epr;
+               epr_write &= STM_USB_EPR_PRESERVE_MASK;
+               epr_write |= STM_USB_EPR_INVARIANT;
+               epr_write &= ~(1 << STM_USB_EPR_CTR_RX);
+               epr_write &= ~(1 << STM_USB_EPR_CTR_TX);
+               stm_usb.epr[ep] = epr_write;
+
+               switch (ep) {
+               case 0:
+                       ++control_count;
+                       if (ao_usb_epr_ctr_rx(epr)) {
+                               if (ao_usb_epr_setup(epr))
+                                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_SETUP;
+                               else
+                                       ao_usb_ep0_receive |= AO_USB_EP0_GOT_RX_DATA;
+                       }
+                       if (ao_usb_epr_ctr_tx(epr))
+                               ao_usb_ep0_receive |= AO_USB_EP0_GOT_TX_ACK;
+                       ao_wakeup(&ao_usb_ep0_receive);
+                       break;
+               case AO_USB_OUT_EPR:
+                       ++out_count;
+                       if (ao_usb_epr_ctr_rx(epr)) {
+                               ao_usb_out_avail = 1;
+                               ao_wakeup(&ao_stdin_ready);
+                       }
+                       break;
+               case AO_USB_IN_EPR:
+                       ++in_count;
+                       if (ao_usb_epr_ctr_tx(epr)) {
+                               ao_usb_in_pending = 0;
+                               ao_wakeup(&ao_usb_in_pending);
+                       }
+                       break;
+               case AO_USB_INT_EPR:
+                       ++int_count;
+                       if (ao_usb_epr_ctr_tx(epr))
+                               _ao_usb_set_stat_tx(AO_USB_INT_EPR, STM_USB_EPR_STAT_TX_NAK);
+                       break;
+               }
+               return;
+       }
+
+       if (istr & (1 << STM_USB_ISTR_RESET)) {
+               ++reset_count;
+               stm_usb.istr &= ~(1 << STM_USB_ISTR_RESET);
+               ao_usb_ep0_receive |= AO_USB_EP0_GOT_RESET;
+               ao_wakeup(&ao_usb_ep0_receive);
+       }
+}
+
+void
+stm_usb_fs_wkup(void)
+{
+       /* USB wakeup, just clear the bit for now */
+       stm_usb.istr &= ~(1 << STM_USB_ISTR_WKUP);
+}
+
+/* The USB memory holds 16 bit values on 32 bit boundaries
+ * and must be accessed only in 32 bit units. Sigh.
+ */
+
+static inline void
+ao_usb_write_byte(uint8_t byte, uint32_t *base, uint16_t offset)
+{
+       base += offset >> 1;
+       if (offset & 1) {
+               *base = (*base & 0xff) | ((uint32_t) byte << 8);
+       } else {
+               *base = (*base & 0xff00) | byte;
+       }
+}
+
+static inline void
+ao_usb_write_short(uint16_t data, uint32_t *base, uint16_t offset)
+{
+       base[offset>>1] = data;
+}
+
+static void
+ao_usb_write(const uint8_t *src, uint32_t *base, uint16_t offset, uint16_t bytes)
+{
+       if (!bytes)
+               return;
+       if (offset & 1) {
+               debug_data (" %02x", src[0]);
+               ao_usb_write_byte(*src++, base, offset++);
+               bytes--;
+       }
+       while (bytes >= 2) {
+               debug_data (" %02x %02x", src[0], src[1]);
+               ao_usb_write_short((src[1] << 8) | src[0], base, offset);
+               offset += 2;
+               src += 2;
+               bytes -= 2;
+       }
+       if (bytes) {
+               debug_data (" %02x", src[0]);
+               ao_usb_write_byte(*src, base, offset);
+       }
+}
+
+static inline uint8_t
+ao_usb_read_byte(uint32_t *base, uint16_t offset)
+{
+       base += offset >> 1;
+       if (offset & 1)
+               return (*base >> 8) & 0xff;
+       else
+               return *base & 0xff;
+}
+
+static inline uint16_t
+ao_usb_read_short(uint32_t *base, uint16_t offset)
+{
+       return base[offset>>1];
+}
+
+static void
+ao_usb_read(uint8_t *dst, uint32_t *base, uint16_t offset, uint16_t bytes)
+{
+       if (!bytes)
+               return;
+       if (offset & 1) {
+               *dst++ = ao_usb_read_byte(base, offset++);
+               debug_data (" %02x", dst[-1]);
+               bytes--;
+       }
+       while (bytes >= 2) {
+               uint16_t        s = ao_usb_read_short(base, offset);
+               dst[0] = s;
+               dst[1] = s >> 8;
+               debug_data (" %02x %02x", dst[0], dst[1]);
+               offset += 2;
+               dst += 2;
+               bytes -= 2;
+       }
+       if (bytes) {
+               *dst = ao_usb_read_byte(base, offset);
+               debug_data (" %02x", dst[0]);
+       }
+}
+
+/* Send an IN data packet */
+static void
+ao_usb_ep0_flush(void)
+{
+       uint8_t this_len;
+
+       /* Check to see if the endpoint is still busy */
+       if (ao_usb_epr_stat_tx(stm_usb.epr[0]) == STM_USB_EPR_STAT_TX_VALID) {
+               debug("EP0 not accepting IN data\n");
+               return;
+       }
+
+       this_len = ao_usb_ep0_in_len;
+       if (this_len > AO_USB_CONTROL_SIZE)
+               this_len = AO_USB_CONTROL_SIZE;
+
+       if (this_len < AO_USB_CONTROL_SIZE)
+               ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_ep0_in_len -= this_len;
+
+       debug_data ("Flush EP0 len %d:", this_len);
+       ao_usb_write(ao_usb_ep0_in_data, ao_usb_ep0_tx_buffer, 0, this_len);
+       debug_data ("\n");
+       ao_usb_ep0_in_data += this_len;
+
+       /* Mark the endpoint as TX valid to send the packet */
+       ao_usb_bdt[AO_USB_CONTROL_EPR].single.count_tx = this_len;
+       ao_usb_set_stat_tx(AO_USB_CONTROL_EPR, STM_USB_EPR_STAT_TX_VALID);
+       debug ("queue tx. epr 0 now %08x\n", stm_usb.epr[AO_USB_CONTROL_EPR]);
+}
+
+/* Read data from the ep0 OUT fifo */
+static void
+ao_usb_ep0_fill(void)
+{
+       uint16_t        len = ao_usb_bdt[0].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK;
+
+       if (len > ao_usb_ep0_out_len)
+               len = ao_usb_ep0_out_len;
+       ao_usb_ep0_out_len -= len;
+
+       /* Pull all of the data out of the packet */
+       debug_data ("Fill EP0 len %d:", len);
+       ao_usb_read(ao_usb_ep0_out_data, ao_usb_ep0_rx_buffer, 0, len);
+       debug_data ("\n");
+       ao_usb_ep0_out_data += len;
+
+       /* ACK the packet */
+       ao_usb_set_stat_rx(0, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static void
+ao_usb_ep0_in_reset(void)
+{
+       ao_usb_ep0_in_data = ao_usb_ep0_in_buf;
+       ao_usb_ep0_in_len = 0;
+}
+
+static void
+ao_usb_ep0_in_queue_byte(uint8_t a)
+{
+       if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_buf))
+               ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a;
+}
+
+static void
+ao_usb_ep0_in_set(const uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_in_data = data;
+       ao_usb_ep0_in_len = len;
+}
+
+static void
+ao_usb_ep0_out_set(uint8_t *data, uint8_t len)
+{
+       ao_usb_ep0_out_data = data;
+       ao_usb_ep0_out_len = len;
+}
+
+static void
+ao_usb_ep0_in_start(uint8_t max)
+{
+       /* Don't send more than asked for */
+       if (ao_usb_ep0_in_len > max)
+               ao_usb_ep0_in_len = max;
+       ao_usb_ep0_flush();
+}
+
+static struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8};
+
+/* Walk through the list of descriptors and find a match
+ */
+static void
+ao_usb_get_descriptor(uint16_t value)
+{
+       const uint8_t           *descriptor;
+       uint8_t         type = value >> 8;
+       uint8_t         index = value;
+
+       descriptor = ao_usb_descriptors;
+       while (descriptor[0] != 0) {
+               if (descriptor[1] == type && index-- == 0) {
+                       uint8_t len;
+                       if (type == AO_USB_DESC_CONFIGURATION)
+                               len = descriptor[2];
+                       else
+                               len = descriptor[0];
+                       ao_usb_ep0_in_set(descriptor, len);
+                       break;
+               }
+               descriptor += descriptor[0];
+       }
+}
+
+static void
+ao_usb_ep0_setup(void)
+{
+       /* Pull the setup packet out of the fifo */
+       ao_usb_ep0_out_set((uint8_t *) &ao_usb_setup, 8);
+       ao_usb_ep0_fill();
+       if (ao_usb_ep0_out_len != 0) {
+               debug ("invalid setup packet length\n");
+               return;
+       }
+
+       if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0)
+               ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+       else
+               ao_usb_ep0_state = AO_USB_EP0_DATA_OUT;
+
+       ao_usb_ep0_in_reset();
+
+       switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) {
+       case AO_USB_TYPE_STANDARD:
+               debug ("Standard setup packet\n");
+               switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) {
+               case AO_USB_RECIP_DEVICE:
+                       debug ("Device setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               debug ("get status\n");
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_ADDRESS:
+                               debug ("set address %d\n", ao_usb_setup.value);
+                               ao_usb_address = ao_usb_setup.value;
+                               ao_usb_address_pending = 1;
+                               break;
+                       case AO_USB_REQ_GET_DESCRIPTOR:
+                               debug ("get descriptor %d\n", ao_usb_setup.value);
+                               ao_usb_get_descriptor(ao_usb_setup.value);
+                               break;
+                       case AO_USB_REQ_GET_CONFIGURATION:
+                               debug ("get configuration %d\n", ao_usb_configuration);
+                               ao_usb_ep0_in_queue_byte(ao_usb_configuration);
+                               break;
+                       case AO_USB_REQ_SET_CONFIGURATION:
+                               ao_usb_configuration = ao_usb_setup.value;
+                               debug ("set configuration %d\n", ao_usb_configuration);
+                               ao_usb_set_configuration();
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_INTERFACE:
+                       debug ("Interface setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_GET_INTERFACE:
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       case AO_USB_REQ_SET_INTERFACE:
+                               break;
+                       }
+                       break;
+               case AO_USB_RECIP_ENDPOINT:
+                       debug ("Endpoint setup packet\n");
+                       switch(ao_usb_setup.request) {
+                       case AO_USB_REQ_GET_STATUS:
+                               ao_usb_ep0_in_queue_byte(0);
+                               ao_usb_ep0_in_queue_byte(0);
+                               break;
+                       }
+                       break;
+               }
+               break;
+       case AO_USB_TYPE_CLASS:
+               debug ("Class setup packet\n");
+               switch (ao_usb_setup.request) {
+               case AO_USB_SET_LINE_CODING:
+                       debug ("set line coding\n");
+                       ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_GET_LINE_CODING:
+                       debug ("get line coding\n");
+                       ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7);
+                       break;
+               case AO_USB_SET_CONTROL_LINE_STATE:
+                       break;
+               }
+               break;
+       }
+
+       /* If we're not waiting to receive data from the host,
+        * queue an IN response
+        */
+       if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+               ao_usb_ep0_in_start(ao_usb_setup.length);
+}
+
+/* End point 0 receives all of the control messages. */
+static void
+ao_usb_ep0(void)
+{
+       uint8_t intx, udint;
+
+       debug ("usb task started\n");
+       ao_usb_ep0_state = AO_USB_EP0_IDLE;
+       for (;;) {
+               uint8_t receive;
+               ao_arch_critical(
+                       while (!(receive = ao_usb_ep0_receive))
+                               ao_sleep(&ao_usb_ep0_receive);
+                       ao_usb_ep0_receive = 0;
+                       );
+               
+               if (receive & AO_USB_EP0_GOT_RESET) {
+                       debug ("\treset\n");
+                       ao_usb_set_ep0();
+                       continue;
+               }
+               if (receive & AO_USB_EP0_GOT_SETUP) {
+                       debug ("\tsetup\n");
+                       ao_usb_ep0_setup();
+               }
+               if (receive & AO_USB_EP0_GOT_RX_DATA) {
+                       debug ("\tgot rx data\n");
+                       if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) {
+                               ao_usb_ep0_fill();
+                               if (ao_usb_ep0_out_len == 0) {
+                                       ao_usb_ep0_state = AO_USB_EP0_DATA_IN;
+                                       ao_usb_ep0_in_start(0);
+                               }
+                       }
+               }
+               if (receive & AO_USB_EP0_GOT_TX_ACK) {
+                       debug ("\tgot tx ack\n");
+
+                       /* Wait until the IN packet is received from addr 0
+                        * before assigning our local address
+                        */
+                       if (ao_usb_address_pending)
+                               ao_usb_set_address(ao_usb_address);
+                       if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN)
+                               ao_usb_ep0_flush();
+               }
+       }
+}
+
+/* Queue the current IN buffer for transmission */
+static void
+ao_usb_in_send(void)
+{
+       debug ("send %d\n", ao_usb_tx_count);
+       ao_usb_in_pending = 1;
+       ao_usb_write(ao_usb_tx_buffer, ao_usb_in_tx_buffer, 0, ao_usb_tx_count);
+       ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = ao_usb_tx_count;
+       ao_usb_set_stat_tx(AO_USB_IN_EPR, STM_USB_EPR_STAT_TX_VALID);
+       ao_usb_tx_count = 0;
+}
+
+/* Wait for a free IN buffer */
+static void
+ao_usb_in_wait(void)
+{
+       for (;;) {
+               /* Check if the current buffer is writable */
+               if (ao_usb_tx_count < AO_USB_IN_SIZE)
+                       break;
+
+               cli();
+               /* Wait for an IN buffer to be ready */
+               while (ao_usb_in_pending)
+                       ao_sleep(&ao_usb_in_pending);
+               sei();
+       }
+}
+
+void
+ao_usb_flush(void) __critical
+{
+       if (!ao_usb_running)
+               return;
+
+       /* Anytime we've sent a character since
+        * the last time we flushed, we'll need
+        * to send a packet -- the only other time
+        * we would send a packet is when that
+        * packet was full, in which case we now
+        * want to send an empty packet
+        */
+       if (!ao_usb_in_flushed) {
+               ao_usb_in_flushed = 1;
+               cli();
+               /* Wait for an IN buffer to be ready */
+               while (ao_usb_in_pending)
+                       ao_sleep(&ao_usb_in_pending);
+               sei();
+               ao_usb_in_send();
+       }
+}
+
+void
+ao_usb_putchar(char c) __critical __reentrant
+{
+       if (!ao_usb_running)
+               return;
+
+       ao_usb_in_wait();
+
+       ao_usb_in_flushed = 0;
+       ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c;
+
+       /* Send the packet when full */
+       if (ao_usb_tx_count == AO_USB_IN_SIZE)
+               ao_usb_in_send();
+}
+
+static void
+ao_usb_out_recv(void)
+{
+       ao_usb_out_avail = 0;
+
+       ao_usb_rx_count = ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx & STM_USB_BDT_COUNT_RX_COUNT_RX_MASK;
+
+       debug ("recv %d\n", ao_usb_rx_count);
+       debug_data("Fill OUT len %d:", ao_usb_rx_count);
+       ao_usb_read(ao_usb_rx_buffer, ao_usb_out_rx_buffer, 0, ao_usb_rx_count);
+       debug_data("\n");
+       ao_usb_rx_pos = 0;
+
+       /* ACK the packet */
+       ao_usb_set_stat_rx(AO_USB_OUT_EPR, STM_USB_EPR_STAT_RX_VALID);
+}
+
+static char
+_ao_usb_pollchar(void)
+{
+       char c;
+
+       if (!ao_usb_running)
+               return AO_READ_AGAIN;
+
+       for (;;) {
+               if (ao_usb_rx_pos != ao_usb_rx_count)
+                       break;
+
+               /* Check to see if a packet has arrived */
+               if (!ao_usb_out_avail)
+                       return AO_READ_AGAIN;
+               ao_usb_out_recv();
+       }
+
+       /* Pull a character out of the fifo */
+       c = ao_usb_rx_buffer[ao_usb_rx_pos++];
+       return c;
+}
+
+char
+ao_usb_pollchar(void)
+{
+       char    c;
+       cli();
+       c = _ao_usb_pollchar();
+       sei();
+       return c;
+}
+
+char
+ao_usb_getchar(void) __critical
+{
+       char    c;
+
+       cli();
+       while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN)
+               ao_sleep(&ao_stdin_ready);
+       sei();
+       return c;
+}
+
+void
+ao_usb_disable(void)
+{
+       stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+       stm_usb.istr = 0;
+
+       /* Disable USB pull-up */
+       stm_syscfg.pmc &= ~(1 << STM_SYSCFG_PMC_USB_PU);
+
+       /* Switch off the device */
+       stm_usb.cntr = (1 << STM_USB_CNTR_PDWN) | (1 << STM_USB_CNTR_FRES);
+
+       /* Disable the interface */
+       stm_rcc.apb1enr &+ ~(1 << STM_RCC_APB1ENR_USBEN);
+}
+
+void
+ao_usb_enable(void)
+{
+       int     t;
+
+       /* Enable SYSCFG */
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGEN);
+
+       /* Disable USB pull-up */
+       stm_syscfg.pmc &= ~(1 << STM_SYSCFG_PMC_USB_PU);
+
+       /* Enable USB device */
+       stm_rcc.apb1enr |= (1 << STM_RCC_APB1ENR_USBEN);
+
+       /* Do not touch the GPIOA configuration; USB takes priority
+        * over GPIO on pins A11 and A12, but if you select alternate
+        * input 10 (the documented correct selection), then USB is
+        * pulled low and doesn't work at all
+        */
+
+       /* Route interrupts */
+       stm_nvic_set_priority(STM_ISR_USB_LP_POS, 3);
+       stm_nvic_set_enable(STM_ISR_USB_LP_POS);
+
+       ao_usb_configuration = 0;
+
+       stm_usb.cntr = (1 << STM_USB_CNTR_FRES);
+
+       /* Clear the power down bit */
+       stm_usb.cntr = 0;
+
+       /* Clear any spurious interrupts */
+       stm_usb.istr = 0;
+       
+       debug ("ao_usb_enable\n");
+
+       /* Enable interrupts */
+       stm_usb.cntr = ((1 << STM_USB_CNTR_CTRM) |
+                       (0 << STM_USB_CNTR_PMAOVRM) |
+                       (0 << STM_USB_CNTR_ERRM) |
+                       (0 << STM_USB_CNTR_WKUPM) |
+                       (0 << STM_USB_CNTR_SUSPM) |
+                       (1 << STM_USB_CNTR_RESETM) |
+                       (0 << STM_USB_CNTR_SOFM) |
+                       (0 << STM_USB_CNTR_ESOFM) |
+                       (0 << STM_USB_CNTR_RESUME) |
+                       (0 << STM_USB_CNTR_FSUSP) |
+                       (0 << STM_USB_CNTR_LP_MODE) |
+                       (0 << STM_USB_CNTR_PDWN) |
+                       (0 << STM_USB_CNTR_FRES));
+
+       for (t = 0; t < 1000; t++)
+               ao_arch_nop();
+       /* Enable USB pull-up */
+       stm_syscfg.pmc |= (1 << STM_SYSCFG_PMC_USB_PU);
+}
+
+#if USB_ECHO
+struct ao_task ao_usb_echo_task;
+
+static void
+ao_usb_echo(void)
+{
+       char    c;
+
+       for (;;) {
+               c = ao_usb_getchar();
+               ao_usb_putchar(c);
+               ao_usb_flush();
+       }
+}
+#endif
+
+#if USB_DEBUG
+static void
+ao_usb_irq(void)
+{
+       printf ("control: %d out: %d in: %d int: %d reset: %d\n",
+               control_count, out_count, in_count, int_count, reset_count);
+}
+
+__code struct ao_cmds ao_usb_cmds[] = {
+       { ao_usb_irq, "I\0Show USB interrupt counts" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_usb_init(void)
+{
+       ao_usb_enable();
+
+       debug ("ao_usb_init\n");
+       ao_add_task(&ao_usb_task, ao_usb_ep0, "usb");
+#if USB_ECHO
+       ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
+#endif
+#if USB_DEBUG
+       ao_cmd_register(&ao_usb_cmds[0]);
+#endif
+#if !USB_ECHO
+       ao_add_stdio(ao_usb_pollchar, ao_usb_putchar, ao_usb_flush);
+#endif
+}
diff --git a/src/stm/registers.ld b/src/stm/registers.ld
new file mode 100644 (file)
index 0000000..fd61e48
--- /dev/null
@@ -0,0 +1,50 @@
+stm_fsmc   = 0xa0000000;
+stm_aes    = 0x50060000;
+stm_dma    = 0x40026000;
+stm_flash  = 0x40023c00;
+stm_rcc    = 0x40023800;
+stm_crc    = 0x40023000;
+stm_gpioh  = 0x40021400;
+stm_gpioe  = 0x40021000;
+stm_gpiod  = 0x40020c00;
+stm_gpioc  = 0x40020800;
+stm_gpiob  = 0x40020400;
+stm_gpioa  = 0x40020000;
+stm_usart1 = 0x40013800;
+stm_spi1   = 0x40013000;
+stm_sdio   = 0x40012c00;
+stm_adc    = 0x40012400;
+stm_tim11  = 0x40011000;
+stm_tim10  = 0x40010c00;
+stm_tim9   = 0x40010800;
+stm_exti   = 0x40010400;
+stm_syscfg = 0x40010000;
+stm_comp   = 0x40007c00;
+stm_ri     = 0x40007c04;
+stm_dac    = 0x40007400;
+stm_pwr    = 0x40007000;
+stm_usb_sram = 0x40006000;
+stm_usb    = 0x40005c00;
+stm_i2c2   = 0x40005800;
+stm_i2c1   = 0x40005400;
+stm_usart5 = 0x40005000;
+stm_usart4 = 0x40004c00;
+stm_usart3 = 0x40004800;
+stm_usart2 = 0x40004400;
+stm_spi3   = 0x40003c00;       /* docs are broken here */
+stm_spi2   = 0x40003800;       /* docs are broken here */
+stm_iwdg   = 0x40003000;
+stm_wwdg   = 0x40002c00;
+stm_rtc    = 0x40002800;
+stm_lcd    = 0x40002400;
+stm_tim7   = 0x40001400;
+stm_tim6   = 0x40001000;
+stm_tim5   = 0x40000c00;
+stm_tim4   = 0x40000800;
+stm_tim3   = 0x40000400;
+stm_tim2   = 0x40000000;
+
+stm_nvic   = 0xe000e100;
+
+/* calibration data in system memory */
+stm_temp_cal = 0x1ff80078;
diff --git a/src/stm/stm32l.h b/src/stm/stm32l.h
new file mode 100644 (file)
index 0000000..25f5af0
--- /dev/null
@@ -0,0 +1,1658 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _STM32L_H_
+#define _STM32L_H_
+
+#include <stdint.h>
+
+typedef volatile uint32_t      vuint32_t;
+typedef volatile void *                vvoid_t;
+
+struct stm_gpio {
+       vuint32_t       moder;
+       vuint32_t       otyper;
+       vuint32_t       ospeedr;
+       vuint32_t       pupdr;
+
+       vuint32_t       idr;
+       vuint32_t       odr;
+       vuint32_t       bsrr;
+       vuint32_t       lckr;
+
+       vuint32_t       afrl;
+       vuint32_t       afrh;
+};
+
+#define STM_MODER_SHIFT(pin)           ((pin) << 1)
+#define STM_MODER_MASK                 3
+#define STM_MODER_INPUT                        0
+#define STM_MODER_OUTPUT               1
+#define STM_MODER_ALTERNATE            2
+#define STM_MODER_ANALOG               3
+
+static inline void
+stm_moder_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+       gpio->moder = ((gpio->moder &
+                       ~(STM_MODER_MASK << STM_MODER_SHIFT(pin))) |
+                      value << STM_MODER_SHIFT(pin));
+}
+       
+static inline vuint32_t
+stm_moder_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->moder >> STM_MODER_SHIFT(pin)) & STM_MODER_MASK;
+}
+
+#define STM_OTYPER_SHIFT(pin)          (pin)
+#define STM_OTYPER_MASK                        1
+#define STM_OTYPER_PUSH_PULL           0
+#define STM_OTYPER_OPEN_DRAIN          1
+
+static inline void
+stm_otyper_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+       gpio->otyper = ((gpio->otyper &
+                        ~(STM_OTYPER_MASK << STM_OTYPER_SHIFT(pin))) |
+                       value << STM_OTYPER_SHIFT(pin));
+}
+       
+static inline vuint32_t
+stm_otyper_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->otyper >> STM_OTYPER_SHIFT(pin)) & STM_OTYPER_MASK;
+}
+
+#define STM_OSPEEDR_SHIFT(pin)         ((pin) << 1)
+#define STM_OSPEEDR_MASK               3
+#define STM_OSPEEDR_400kHz             0
+#define STM_OSPEEDR_2MHz               1
+#define STM_OSPEEDR_10MHz              2
+#define STM_OSPEEDR_40MHz              3
+
+static inline void
+stm_ospeedr_set(struct stm_gpio *gpio, int pin, vuint32_t value) {
+       gpio->ospeedr = ((gpio->ospeedr &
+                       ~(STM_OSPEEDR_MASK << STM_OSPEEDR_SHIFT(pin))) |
+                      value << STM_OSPEEDR_SHIFT(pin));
+}
+       
+static inline vuint32_t
+stm_ospeedr_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->ospeedr >> STM_OSPEEDR_SHIFT(pin)) & STM_OSPEEDR_MASK;
+}
+
+#define STM_PUPDR_SHIFT(pin)           ((pin) << 1)
+#define STM_PUPDR_MASK                 3
+#define STM_PUPDR_NONE                 0
+#define STM_PUPDR_PULL_UP              1
+#define STM_PUPDR_PULL_DOWN            2
+#define STM_PUPDR_RESERVED             3
+
+static inline void
+stm_pupdr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
+       gpio->pupdr = ((gpio->pupdr &
+                       ~(STM_PUPDR_MASK << STM_PUPDR_SHIFT(pin))) |
+                      value << STM_PUPDR_SHIFT(pin));
+}
+       
+static inline uint32_t
+stm_pupdr_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->pupdr >> STM_PUPDR_SHIFT(pin)) & STM_PUPDR_MASK;
+}
+
+#define STM_AFR_SHIFT(pin)             ((pin) << 2)
+#define STM_AFR_MASK                   0xf
+#define STM_AFR_NONE                   0
+#define STM_AFR_AF0                    0x0
+#define STM_AFR_AF1                    0x1
+#define STM_AFR_AF2                    0x2
+#define STM_AFR_AF3                    0x3
+#define STM_AFR_AF4                    0x4
+#define STM_AFR_AF5                    0x5
+#define STM_AFR_AF6                    0x6
+#define STM_AFR_AF7                    0x7
+#define STM_AFR_AF8                    0x8
+#define STM_AFR_AF9                    0x9
+#define STM_AFR_AF10                   0xa
+#define STM_AFR_AF11                   0xb
+#define STM_AFR_AF12                   0xc
+#define STM_AFR_AF13                   0xd
+#define STM_AFR_AF14                   0xe
+#define STM_AFR_AF15                   0xf
+
+static inline void
+stm_afr_set(struct stm_gpio *gpio, int pin, uint32_t value) {
+       /*
+        * Set alternate pin mode too
+        */
+       stm_moder_set(gpio, pin, STM_MODER_ALTERNATE);
+       if (pin < 8)
+               gpio->afrl = ((gpio->afrl &
+                              ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) |
+                             value << STM_AFR_SHIFT(pin));
+       else {
+               pin -= 8;
+               gpio->afrh = ((gpio->afrh &
+                              ~(STM_AFR_MASK << STM_AFR_SHIFT(pin))) |
+                             value << STM_AFR_SHIFT(pin));
+       }
+}
+       
+static inline uint32_t
+stm_afr_get(struct stm_gpio *gpio, int pin) {
+       if (pin < 8)
+               return (gpio->afrl >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK;
+       else {
+               pin -= 8;
+               return (gpio->afrh >> STM_AFR_SHIFT(pin)) & STM_AFR_MASK;
+       }
+}
+
+static inline void
+stm_gpio_set(struct stm_gpio *gpio, int pin, uint8_t value) {
+       /* Use the bit set/reset register to do this atomically */
+       gpio->bsrr = ((uint32_t) (value ^ 1) << (pin + 16)) | ((uint32_t) value << pin);
+}
+
+static inline uint8_t
+stm_gpio_get(struct stm_gpio *gpio, int pin) {
+       return (gpio->idr >> pin) & 1;
+}
+
+extern struct stm_gpio stm_gpioa;
+extern struct stm_gpio stm_gpiob;
+extern struct stm_gpio stm_gpioc;
+extern struct stm_gpio stm_gpiod;
+extern struct stm_gpio stm_gpioe;
+extern struct stm_gpio stm_gpioh;
+
+struct stm_usart {
+       vuint32_t       sr;     /* status register */
+       vuint32_t       dr;     /* data register */
+       vuint32_t       brr;    /* baud rate register */
+       vuint32_t       cr1;    /* control register 1 */
+
+       vuint32_t       cr2;    /* control register 2 */
+       vuint32_t       cr3;    /* control register 3 */
+       vuint32_t       gtpr;   /* guard time and prescaler */
+};
+
+extern struct stm_usart        stm_usart1;
+extern struct stm_usart stm_usart2;
+extern struct stm_usart stm_usart3;
+
+#define STM_USART_SR_CTS       (9)     /* CTS flag */
+#define STM_USART_SR_LBD       (8)     /* LIN break detection flag */
+#define STM_USART_SR_TXE       (7)     /* Transmit data register empty */
+#define STM_USART_SR_TC                (6)     /* Transmission complete */
+#define STM_USART_SR_RXNE      (5)     /* Read data register not empty */
+#define STM_USART_SR_IDLE      (4)     /* IDLE line detected */
+#define STM_USART_SR_ORE       (3)     /* Overrun error */
+#define STM_USART_SR_NF                (2)     /* Noise detected flag */
+#define STM_USART_SR_FE                (1)     /* Framing error */
+#define STM_USART_SR_PE                (0)     /* Parity error */
+
+#define STM_USART_CR1_OVER8    (15)    /* Oversampling mode */
+#define STM_USART_CR1_UE       (13)    /* USART enable */
+#define STM_USART_CR1_M                (12)    /* Word length */
+#define STM_USART_CR1_WAKE     (11)    /* Wakeup method */
+#define STM_USART_CR1_PCE      (10)    /* Parity control enable */
+#define STM_USART_CR1_PS       (9)     /* Parity selection */
+#define STM_USART_CR1_PEIE     (8)     /* PE interrupt enable */
+#define STM_USART_CR1_TXEIE    (7)     /* TXE interrupt enable */
+#define STM_USART_CR1_TCIE     (6)     /* Transmission complete interrupt enable */
+#define STM_USART_CR1_RXNEIE   (5)     /* RXNE interrupt enable */
+#define STM_USART_CR1_IDLEIE   (4)     /* IDLE interrupt enable */
+#define STM_USART_CR1_TE       (3)     /* Transmitter enable */
+#define STM_USART_CR1_RE       (2)     /* Receiver enable */
+#define STM_USART_CR1_RWU      (1)     /* Receiver wakeup */
+#define STM_USART_CR1_SBK      (0)     /* Send break */
+
+#define STM_USART_CR2_LINEN    (14)    /* LIN mode enable */
+#define STM_USART_CR2_STOP     (12)    /* STOP bits */
+#define STM_USART_CR2_STOP_MASK        3
+#define STM_USART_CR2_STOP_1   0
+#define STM_USART_CR2_STOP_0_5 1
+#define STM_USART_CR2_STOP_2   2
+#define STM_USART_CR2_STOP_1_5 3
+
+#define STM_USART_CR2_CLKEN    (11)    /* Clock enable */
+#define STM_USART_CR2_CPOL     (10)    /* Clock polarity */
+#define STM_USART_CR2_CPHA     (9)     /* Clock phase */
+#define STM_USART_CR2_LBCL     (8)     /* Last bit clock pulse */
+#define STM_USART_CR2_LBDIE    (6)     /* LIN break detection interrupt enable */
+#define STM_USART_CR2_LBDL     (5)     /* lin break detection length */
+#define STM_USART_CR2_ADD      (0)
+#define STM_USART_CR2_ADD_MASK 0xf
+
+#define STM_USART_CR3_ONEBITE  (11)    /* One sample bit method enable */
+#define STM_USART_CR3_CTSIE    (10)    /* CTS interrupt enable */
+#define STM_USART_CR3_CTSE     (9)     /* CTS enable */
+#define STM_USART_CR3_RTSE     (8)     /* RTS enable */
+#define STM_USART_CR3_DMAT     (7)     /* DMA enable transmitter */
+#define STM_USART_CR3_DMAR     (6)     /* DMA enable receiver */
+#define STM_USART_CR3_SCEN     (5)     /* Smartcard mode enable */
+#define STM_USART_CR3_NACK     (4)     /* Smartcard NACK enable */
+#define STM_USART_CR3_HDSEL    (3)     /* Half-duplex selection */
+#define STM_USART_CR3_IRLP     (2)     /* IrDA low-power */
+#define STM_USART_CR3_IREN     (1)     /* IrDA mode enable */
+#define STM_USART_CR3_EIE      (0)     /* Error interrupt enable */
+
+struct stm_tim {
+};
+
+extern struct stm_tim stm_tim9;
+extern struct stm_tim stm_tim10;
+extern struct stm_tim stm_tim11;
+
+/* Flash interface */
+
+struct stm_flash {
+       vuint32_t       acr;
+       vuint32_t       pecr;
+       vuint32_t       pdkeyr;
+       vuint32_t       pekeyr;
+
+       vuint32_t       prgkeyr;
+       vuint32_t       optkeyr;
+       vuint32_t       sr;
+       vuint32_t       obr;
+
+       vuint32_t       wrpr;
+};
+
+extern struct stm_flash        stm_flash;
+
+#define STM_FLASH_ACR_RUN_PD   (4)
+#define STM_FLASH_ACR_SLEEP_PD (3)
+#define STM_FLASH_ACR_ACC64    (2)
+#define STM_FLASH_ACR_PRFEN    (1)
+#define STM_FLASH_ACR_LATENCY  (0)
+
+#define STM_FLASH_PECR_OBL_LAUNCH      18
+#define STM_FLASH_PECR_ERRIE           17
+#define STM_FLASH_PECR_EOPIE           16
+#define STM_FLASH_PECR_FPRG            10
+#define STM_FLASH_PECR_ERASE           9
+#define STM_FLASH_PECR_FTDW            8
+#define STM_FLASH_PECR_DATA            4
+#define STM_FLASH_PECR_PROG            3
+#define STM_FLASH_PECR_OPTLOCK         2
+#define STM_FLASH_PECR_PRGLOCK         1
+#define STM_FLASH_PECR_PELOCK          0
+
+#define STM_FLASH_SR_OPTVERR           11
+#define STM_FLASH_SR_SIZERR            10
+#define STM_FLASH_SR_PGAERR            9
+#define STM_FLASH_SR_WRPERR            8
+#define STM_FLASH_SR_READY             3
+#define STM_FLASH_SR_ENDHV             2
+#define STM_FLASH_SR_EOP               1
+#define STM_FLASH_SR_BSY               0
+
+#define STM_FLASH_PEKEYR_PEKEY1        0x89ABCDEF
+#define STM_FLASH_PEKEYR_PEKEY2 0x02030405
+
+struct stm_rcc {
+       vuint32_t       cr;
+       vuint32_t       icscr;
+       vuint32_t       cfgr;
+       vuint32_t       cir;
+
+       vuint32_t       ahbrstr;
+       vuint32_t       apb2rstr;
+       vuint32_t       apb1rstr;
+       vuint32_t       ahbenr;
+
+       vuint32_t       apb2enr;
+       vuint32_t       apb1enr;
+       vuint32_t       ahblenr;
+       vuint32_t       apb2lpenr;
+
+       vuint32_t       apb1lpenr;
+       vuint32_t       csr;
+};
+
+extern struct stm_rcc stm_rcc;
+
+/* Nominal high speed internal oscillator frequency is 16MHz */
+#define STM_HSI_FREQ           16000000
+
+#define STM_RCC_CR_RTCPRE      (29)
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_2   0
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_4   1
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_8   2
+#define  STM_RCC_CR_RTCPRE_HSE_DIV_16  3
+#define  STM_RCC_CR_RTCPRE_HSE_MASK    3
+
+#define STM_RCC_CR_CSSON       (28)
+#define STM_RCC_CR_PLLRDY      (25)
+#define STM_RCC_CR_PLLON       (24)
+#define STM_RCC_CR_HSEBYP      (18)
+#define STM_RCC_CR_HSERDY      (17)
+#define STM_RCC_CR_HSEON       (16)
+#define STM_RCC_CR_MSIRDY      (9)
+#define STM_RCC_CR_MSION       (8)
+#define STM_RCC_CR_HSIRDY      (1)
+#define STM_RCC_CR_HSION       (0)
+
+#define STM_RCC_CFGR_MCOPRE    (28)
+#define  STM_RCC_CFGR_MCOPRE_DIV_1     0
+#define  STM_RCC_CFGR_MCOPRE_DIV_2     1
+#define  STM_RCC_CFGR_MCOPRE_DIV_4     2
+#define  STM_RCC_CFGR_MCOPRE_DIV_8     3
+#define  STM_RCC_CFGR_MCOPRE_DIV_16    4
+#define  STM_RCC_CFGR_MCOPRE_DIV_MASK  7
+
+#define STM_RCC_CFGR_MCOSEL    (24)
+#define  STM_RCC_CFGR_MCOSEL_DISABLE   0
+#define  STM_RCC_CFGR_MCOSEL_SYSCLK    1
+#define  STM_RCC_CFGR_MCOSEL_HSI       2
+#define  STM_RCC_CFGR_MCOSEL_MSI       3
+#define  STM_RCC_CFGR_MCOSEL_HSE       4
+#define  STM_RCC_CFGR_MCOSEL_PLL       5
+#define  STM_RCC_CFGR_MCOSEL_LSI       6
+#define  STM_RCC_CFGR_MCOSEL_LSE       7
+#define  STM_RCC_CFGR_MCOSEL_MASK      7
+
+#define STM_RCC_CFGR_PLLDIV    (22)
+#define  STM_RCC_CFGR_PLLDIV_2         1
+#define  STM_RCC_CFGR_PLLDIV_3         2
+#define  STM_RCC_CFGR_PLLDIV_4         3
+#define  STM_RCC_CFGR_PLLDIV_MASK      3
+
+#define STM_RCC_CFGR_PLLMUL    (18)
+#define  STM_RCC_CFGR_PLLMUL_3         0
+#define  STM_RCC_CFGR_PLLMUL_4         1
+#define  STM_RCC_CFGR_PLLMUL_6         2
+#define  STM_RCC_CFGR_PLLMUL_8         3
+#define  STM_RCC_CFGR_PLLMUL_12                4
+#define  STM_RCC_CFGR_PLLMUL_16                5
+#define  STM_RCC_CFGR_PLLMUL_24                6
+#define  STM_RCC_CFGR_PLLMUL_32                7
+#define  STM_RCC_CFGR_PLLMUL_48                8
+#define  STM_RCC_CFGR_PLLMUL_MASK      0xf
+
+#define STM_RCC_CFGR_PLLSRC    (16)
+
+#define STM_RCC_CFGR_PPRE2     (11)
+#define  STM_RCC_CFGR_PPRE2_DIV_1      0
+#define  STM_RCC_CFGR_PPRE2_DIV_2      4
+#define  STM_RCC_CFGR_PPRE2_DIV_4      5
+#define  STM_RCC_CFGR_PPRE2_DIV_8      6
+#define  STM_RCC_CFGR_PPRE2_DIV_16     7
+#define  STM_RCC_CFGR_PPRE2_MASK       7
+
+#define STM_RCC_CFGR_PPRE1     (8)
+#define  STM_RCC_CFGR_PPRE1_DIV_1      0
+#define  STM_RCC_CFGR_PPRE1_DIV_2      4
+#define  STM_RCC_CFGR_PPRE1_DIV_4      5
+#define  STM_RCC_CFGR_PPRE1_DIV_8      6
+#define  STM_RCC_CFGR_PPRE1_DIV_16     7
+#define  STM_RCC_CFGR_PPRE1_MASK       7
+
+#define STM_RCC_CFGR_HPRE      (4)
+#define  STM_RCC_CFGR_HPRE_DIV_1       0
+#define  STM_RCC_CFGR_HPRE_DIV_2       8
+#define  STM_RCC_CFGR_HPRE_DIV_4       9
+#define  STM_RCC_CFGR_HPRE_DIV_8       0xa
+#define  STM_RCC_CFGR_HPRE_DIV_16      0xb
+#define  STM_RCC_CFGR_HPRE_DIV_64      0xc
+#define  STM_RCC_CFGR_HPRE_DIV_128     0xd
+#define  STM_RCC_CFGR_HPRE_DIV_256     0xe
+#define  STM_RCC_CFGR_HPRE_DIV_512     0xf
+#define  STM_RCC_CFGR_HPRE_MASK                0xf
+
+#define STM_RCC_CFGR_SWS       (2)
+#define  STM_RCC_CFGR_SWS_MSI          0
+#define  STM_RCC_CFGR_SWS_HSI          1
+#define  STM_RCC_CFGR_SWS_HSE          2
+#define  STM_RCC_CFGR_SWS_PLL          3
+#define  STM_RCC_CFGR_SWS_MASK         3
+
+#define STM_RCC_CFGR_SW                (0)
+#define  STM_RCC_CFGR_SW_MSI           0
+#define  STM_RCC_CFGR_SW_HSI           1
+#define  STM_RCC_CFGR_SW_HSE           2
+#define  STM_RCC_CFGR_SW_PLL           3
+#define  STM_RCC_CFGR_SW_MASK          3
+
+#define STM_RCC_AHBENR_DMA1EN          (24)
+#define STM_RCC_AHBENR_FLITFEN         (15)
+#define STM_RCC_AHBENR_CRCEN           (12)
+#define STM_RCC_AHBENR_GPIOHEN         (5)
+#define STM_RCC_AHBENR_GPIOEEN         (4)
+#define STM_RCC_AHBENR_GPIODEN         (3)
+#define STM_RCC_AHBENR_GPIOCEN         (2)
+#define STM_RCC_AHBENR_GPIOBEN         (1)
+#define STM_RCC_AHBENR_GPIOAEN         (0)
+
+#define STM_RCC_APB2ENR_USART1EN       (14)
+#define STM_RCC_APB2ENR_SPI1EN         (12)
+#define STM_RCC_APB2ENR_ADC1EN         (9)
+#define STM_RCC_APB2ENR_TIM11EN                (4)
+#define STM_RCC_APB2ENR_TIM10EN                (3)
+#define STM_RCC_APB2ENR_TIM9EN         (2)
+#define STM_RCC_APB2ENR_SYSCFGEN       (0)
+
+#define STM_RCC_APB1ENR_COMPEN         (31)
+#define STM_RCC_APB1ENR_DACEN          (29)
+#define STM_RCC_APB1ENR_PWREN          (28)
+#define STM_RCC_APB1ENR_USBEN          (23)
+#define STM_RCC_APB1ENR_I2C2EN         (22)
+#define STM_RCC_APB1ENR_I2C1EN         (21)
+#define STM_RCC_APB1ENR_USART3EN       (18)
+#define STM_RCC_APB1ENR_USART2EN       (17)
+#define STM_RCC_APB1ENR_SPI2EN         (14)
+#define STM_RCC_APB1ENR_WWDGEN         (11)
+#define STM_RCC_APB1ENR_LCDEN          (9)
+#define STM_RCC_APB1ENR_TIM7EN         (5)
+#define STM_RCC_APB1ENR_TIM6EN         (4)
+#define STM_RCC_APB1ENR_TIM4EN         (2)
+#define STM_RCC_APB1ENR_TIM3EN         (1)
+#define STM_RCC_APB1ENR_TIM2EN         (0)
+
+#define STM_RCC_CSR_LPWRRSTF           (31)
+#define STM_RCC_CSR_WWDGRSTF           (30)
+#define STM_RCC_CSR_IWDGRSTF           (29)
+#define STM_RCC_CSR_SFTRSTF            (28)
+#define STM_RCC_CSR_PORRSTF            (27)
+#define STM_RCC_CSR_PINRSTF            (26)
+#define STM_RCC_CSR_OBLRSTF            (25)
+#define STM_RCC_CSR_RMVF               (24)
+#define STM_RCC_CSR_RTFRST             (23)
+#define STM_RCC_CSR_RTCEN              (22)
+#define STM_RCC_CSR_RTCSEL             (16)
+
+#define  STM_RCC_CSR_RTCSEL_NONE               0
+#define  STM_RCC_CSR_RTCSEL_LSE                        1
+#define  STM_RCC_CSR_RTCSEL_LSI                        2
+#define  STM_RCC_CSR_RTCSEL_HSE                        3
+#define  STM_RCC_CSR_RTCSEL_MASK               3
+
+#define STM_RCC_CSR_LSEBYP             (10)
+#define STM_RCC_CSR_LSERDY             (9)
+#define STM_RCC_CSR_LSEON              (8)
+#define STM_RCC_CSR_LSIRDY             (1)
+#define STM_RCC_CSR_LSION              (0)
+
+struct stm_pwr {
+       vuint32_t       cr;
+       vuint32_t       csr;
+};
+
+extern struct stm_pwr stm_pwr;
+
+#define STM_PWR_CR_LPRUN       (14)
+
+#define STM_PWR_CR_VOS         (11)
+#define  STM_PWR_CR_VOS_1_8            1
+#define  STM_PWR_CR_VOS_1_5            2
+#define  STM_PWR_CR_VOS_1_2            3
+#define  STM_PWR_CR_VOS_MASK           3
+
+#define STM_PWR_CR_FWU         (10)
+#define STM_PWR_CR_ULP         (9)
+#define STM_PWR_CR_DBP         (8)
+
+#define STM_PWR_CR_PLS         (5)
+#define  STM_PWR_CR_PLS_1_9    0
+#define  STM_PWR_CR_PLS_2_1    1
+#define  STM_PWR_CR_PLS_2_3    2
+#define  STM_PWR_CR_PLS_2_5    3
+#define  STM_PWR_CR_PLS_2_7    4
+#define  STM_PWR_CR_PLS_2_9    5
+#define  STM_PWR_CR_PLS_3_1    6
+#define  STM_PWR_CR_PLS_EXT    7
+#define  STM_PWR_CR_PLS_MASK   7
+
+#define STM_PWR_CR_PVDE                (4)
+#define STM_PWR_CR_CSBF                (3)
+#define STM_PWR_CR_CWUF                (2)
+#define STM_PWR_CR_PDDS                (1)
+#define STM_PWR_CR_LPSDSR      (0)
+
+#define STM_PWR_CSR_EWUP3      (10)
+#define STM_PWR_CSR_EWUP2      (9)
+#define STM_PWR_CSR_EWUP1      (8)
+#define STM_PWR_CSR_REGLPF     (5)
+#define STM_PWR_CSR_VOSF       (4)
+#define STM_PWR_CSR_VREFINTRDYF        (3)
+#define STM_PWR_CSR_PVDO       (2)
+#define STM_PWR_CSR_SBF                (1)
+#define STM_PWR_CSR_WUF                (0)
+
+struct stm_tim67 {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       uint32_t        _unused_08;
+       vuint32_t       dier;
+
+       vuint32_t       sr;
+       vuint32_t       egr;
+       uint32_t        _unused_18;
+       uint32_t        _unused_1c;
+
+       uint32_t        _unused_20;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+};
+
+extern struct stm_tim67 stm_tim6;
+
+#define STM_TIM67_CR1_ARPE     (7)
+#define STM_TIM67_CR1_OPM      (3)
+#define STM_TIM67_CR1_URS      (2)
+#define STM_TIM67_CR1_UDIS     (1)
+#define STM_TIM67_CR1_CEN      (0)
+
+#define STM_TIM67_CR2_MMS      (4)
+#define  STM_TIM67_CR2_MMS_RESET       0
+#define  STM_TIM67_CR2_MMS_ENABLE      1
+#define  STM_TIM67_CR2_MMS_UPDATE      2
+#define  STM_TIM67_CR2_MMS_MASK                7
+
+#define STM_TIM67_DIER_UDE     (8)
+#define STM_TIM67_DIER_UIE     (0)
+
+#define STM_TIM67_SR_UIF       (0)
+
+#define STM_TIM67_EGR_UG       (0)
+
+struct stm_lcd {
+       vuint32_t       cr;
+       vuint32_t       fcr;
+       vuint32_t       sr;
+       vuint32_t       clr;
+       uint32_t        unused_0x10;
+       vuint32_t       ram[8*2];
+};
+
+extern struct stm_lcd stm_lcd;
+
+#define STM_LCD_CR_MUX_SEG             (7)
+
+#define STM_LCD_CR_BIAS                        (5)
+#define  STM_LCD_CR_BIAS_1_4           0
+#define  STM_LCD_CR_BIAS_1_2           1
+#define  STM_LCD_CR_BIAS_1_3           2
+#define  STM_LCD_CR_BIAS_MASK          3
+
+#define STM_LCD_CR_DUTY                        (2)
+#define  STM_LCD_CR_DUTY_STATIC                0
+#define  STM_LCD_CR_DUTY_1_2           1
+#define  STM_LCD_CR_DUTY_1_3           2
+#define  STM_LCD_CR_DUTY_1_4           3
+#define  STM_LCD_CR_DUTY_1_8           4
+#define  STM_LCD_CR_DUTY_MASK          7
+
+#define STM_LCD_CR_VSEL                        (1)
+#define STM_LCD_CR_LCDEN               (0)
+
+#define STM_LCD_FCR_PS                 (22)
+#define  STM_LCD_FCR_PS_1              0x0
+#define  STM_LCD_FCR_PS_2              0x1
+#define  STM_LCD_FCR_PS_4              0x2
+#define  STM_LCD_FCR_PS_8              0x3
+#define  STM_LCD_FCR_PS_16             0x4
+#define  STM_LCD_FCR_PS_32             0x5
+#define  STM_LCD_FCR_PS_64             0x6
+#define  STM_LCD_FCR_PS_128            0x7
+#define  STM_LCD_FCR_PS_256            0x8
+#define  STM_LCD_FCR_PS_512            0x9
+#define  STM_LCD_FCR_PS_1024           0xa
+#define  STM_LCD_FCR_PS_2048           0xb
+#define  STM_LCD_FCR_PS_4096           0xc
+#define  STM_LCD_FCR_PS_8192           0xd
+#define  STM_LCD_FCR_PS_16384          0xe
+#define  STM_LCD_FCR_PS_32768          0xf
+#define  STM_LCD_FCR_PS_MASK           0xf
+
+#define STM_LCD_FCR_DIV                        (18)
+#define STM_LCD_FCR_DIV_16             0x0
+#define STM_LCD_FCR_DIV_17             0x1
+#define STM_LCD_FCR_DIV_18             0x2
+#define STM_LCD_FCR_DIV_19             0x3
+#define STM_LCD_FCR_DIV_20             0x4
+#define STM_LCD_FCR_DIV_21             0x5
+#define STM_LCD_FCR_DIV_22             0x6
+#define STM_LCD_FCR_DIV_23             0x7
+#define STM_LCD_FCR_DIV_24             0x8
+#define STM_LCD_FCR_DIV_25             0x9
+#define STM_LCD_FCR_DIV_26             0xa
+#define STM_LCD_FCR_DIV_27             0xb
+#define STM_LCD_FCR_DIV_28             0xc
+#define STM_LCD_FCR_DIV_29             0xd
+#define STM_LCD_FCR_DIV_30             0xe
+#define STM_LCD_FCR_DIV_31             0xf
+#define STM_LCD_FCR_DIV_MASK           0xf
+
+#define STM_LCD_FCR_BLINK              (16)
+#define  STM_LCD_FCR_BLINK_DISABLE             0
+#define  STM_LCD_FCR_BLINK_SEG0_COM0           1
+#define  STM_LCD_FCR_BLINK_SEG0_COMALL         2
+#define  STM_LCD_FCR_BLINK_SEGALL_COMALL       3
+#define  STM_LCD_FCR_BLINK_MASK                        3
+
+#define STM_LCD_FCR_BLINKF             (13)
+#define  STM_LCD_FCR_BLINKF_8                  0
+#define  STM_LCD_FCR_BLINKF_16                 1
+#define  STM_LCD_FCR_BLINKF_32                 2
+#define  STM_LCD_FCR_BLINKF_64                 3
+#define  STM_LCD_FCR_BLINKF_128                        4
+#define  STM_LCD_FCR_BLINKF_256                        5
+#define  STM_LCD_FCR_BLINKF_512                        6
+#define  STM_LCD_FCR_BLINKF_1024               7
+#define  STM_LCD_FCR_BLINKF_MASK               7
+
+#define STM_LCD_FCR_CC                 (10)
+#define  STM_LCD_FCR_CC_MASK                   7
+
+#define STM_LCD_FCR_DEAD               (7)
+#define  STM_LCD_FCR_DEAD_MASK                 7
+
+#define STM_LCD_FCR_PON                        (4)
+#define  STM_LCD_FCR_PON_MASK                  7
+
+#define STM_LCD_FCR_UDDIE              (3)
+#define STM_LCD_FCR_SOFIE              (1)
+#define STM_LCD_FCR_HD                 (0)
+
+#define STM_LCD_SR_FCRSF               (5)
+#define STM_LCD_SR_RDY                 (4)
+#define STM_LCD_SR_UDD                 (3)
+#define STM_LCD_SR_UDR                 (2)
+#define STM_LCD_SR_SOF                 (1)
+#define STM_LCD_SR_ENS                 (0)
+
+#define STM_LCD_CLR_UDDC               (3)
+#define STM_LCD_CLR_SOFC               (1)
+
+struct stm_nvic {
+       vuint32_t       iser[3];        /* 0x000 */
+
+       uint8_t         _unused00c[0x080 - 0x00c];
+
+       vuint32_t       icer[3];        /* 0x080 */
+
+       uint8_t         _unused08c[0x100 - 0x08c];
+
+       vuint32_t       ispr[3];        /* 0x100 */
+
+       uint8_t         _unused10c[0x180 - 0x10c];
+
+       vuint32_t       icpr[3];        /* 0x180 */
+
+       uint8_t         _unused18c[0x200 - 0x18c];
+
+       vuint32_t       iabr[3];        /* 0x200 */
+
+       uint8_t         _unused20c[0x300 - 0x20c];
+
+       vuint32_t       ipr[21];        /* 0x300 */
+
+       uint8_t         _unused324[0xe00 - 0x324];
+
+       vuint32_t       stir;           /* 0xe00 */
+};
+
+extern struct stm_nvic stm_nvic;
+
+#define IRQ_REG(irq)   ((irq) >> 5)
+#define IRQ_BIT(irq)   ((irq) & 0x1f)
+#define IRQ_MASK(irq)  (1 << IRQ_BIT(irq))
+#define IRQ_BOOL(v,irq)        (((v) >> IRQ_BIT(irq)) & 1)
+
+static inline void
+stm_nvic_set_enable(int irq) {
+       stm_nvic.iser[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_enable(int irq) {
+       stm_nvic.icer[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_enabled(int irq) {
+       return IRQ_BOOL(stm_nvic.iser[IRQ_REG(irq)], irq);
+}
+       
+static inline void
+stm_nvic_set_pending(int irq) {
+       stm_nvic.ispr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline void
+stm_nvic_clear_pending(int irq) {
+       stm_nvic.icpr[IRQ_REG(irq)] = IRQ_MASK(irq);
+}
+
+static inline int
+stm_nvic_pending(int irq) {
+       return IRQ_BOOL(stm_nvic.ispr[IRQ_REG(irq)], irq);
+}
+
+static inline int
+stm_nvic_active(int irq) {
+       return IRQ_BOOL(stm_nvic.iabr[IRQ_REG(irq)], irq);
+}
+
+#define IRQ_PRIO_REG(irq)      ((irq) >> 2)
+#define IRQ_PRIO_BIT(irq)      (((irq) & 3) << 3)
+#define IRQ_PRIO_MASK(irq)     (0xff << IRQ_PRIO_BIT(irq))
+
+static inline void
+stm_nvic_set_priority(int irq, uint8_t prio) {
+       int             n = IRQ_PRIO_REG(irq);
+       uint32_t        v;
+
+       v = stm_nvic.ipr[n];
+       v &= ~IRQ_PRIO_MASK(irq);
+       v |= (prio) << IRQ_PRIO_BIT(irq);
+       stm_nvic.ipr[n] = v;
+}
+
+static inline uint8_t
+stm_nvic_get_priority(int irq) {
+       return (stm_nvic.ipr[IRQ_PRIO_REG(irq)] >> IRQ_PRIO_BIT(irq)) & IRQ_PRIO_MASK(0);
+}
+
+#define isr(name) void stm_ ## name ## _isr(void);
+
+isr(nmi)
+isr(hardfault)
+isr(memmanage)
+isr(busfault)
+isr(usagefault)
+isr(svc)
+isr(debugmon)
+isr(pendsv)
+isr(systick)
+isr(wwdg)
+isr(pvd)
+isr(tamper_stamp)
+isr(rtc_wkup)
+isr(flash)
+isr(rcc)
+isr(exti0)
+isr(exti1)
+isr(exti2)
+isr(exti3)
+isr(exti4)
+isr(dma1_channel1)
+isr(dma1_channel2)
+isr(dma1_channel3)
+isr(dma1_channel4)
+isr(dma1_channel5)
+isr(dma1_channel6)
+isr(dma1_channel7)
+isr(adc1)
+isr(usb_hp)
+isr(usb_lp)
+isr(dac)
+isr(comp)
+isr(exti9_5)
+isr(lcd)
+isr(tim9)
+isr(tim10)
+isr(tim11)
+isr(tim2)
+isr(tim3)
+isr(tim4)
+isr(i2c1_ev)
+isr(i2c1_er)
+isr(i2c2_ev)
+isr(i2c2_er)
+isr(spi1)
+isr(spi2)
+isr(usart1)
+isr(usart2)
+isr(usart3)
+isr(exti15_10)
+isr(rtc_alarm)
+isr(usb_fs_wkup)
+isr(tim6)
+isr(tim7)
+
+#undef isr
+
+#define STM_ISR_WWDG_POS               0
+#define STM_ISR_PVD_POS                        1
+#define STM_ISR_TAMPER_STAMP_POS       2
+#define STM_ISR_RTC_WKUP_POS           3
+#define STM_ISR_FLASH_POS              4
+#define STM_ISR_RCC_POS                        5
+#define STM_ISR_EXTI0_POS              6
+#define STM_ISR_EXTI1_POS              7
+#define STM_ISR_EXTI2_POS              8
+#define STM_ISR_EXTI3_POS              9
+#define STM_ISR_EXTI4_POS              10
+#define STM_ISR_DMA1_CHANNEL1_POS      11
+#define STM_ISR_DMA2_CHANNEL1_POS      12
+#define STM_ISR_DMA3_CHANNEL1_POS      13
+#define STM_ISR_DMA4_CHANNEL1_POS      14
+#define STM_ISR_DMA5_CHANNEL1_POS      15
+#define STM_ISR_DMA6_CHANNEL1_POS      16
+#define STM_ISR_DMA7_CHANNEL1_POS      17
+#define STM_ISR_ADC1_POS               18
+#define STM_ISR_USB_HP_POS             19
+#define STM_ISR_USB_LP_POS             20
+#define STM_ISR_DAC_POS                        21
+#define STM_ISR_COMP_POS               22
+#define STM_ISR_EXTI9_5_POS            23
+#define STM_ISR_LCD_POS                        24
+#define STM_ISR_TIM9_POS               25
+#define STM_ISR_TIM10_POS              26
+#define STM_ISR_TIM11_POS              27
+#define STM_ISR_TIM2_POS               28
+#define STM_ISR_TIM3_POS               29
+#define STM_ISR_TIM4_POS               30
+#define STM_ISR_I2C1_EV_POS            31
+#define STM_ISR_I2C1_ER_POS            32
+#define STM_ISR_I2C2_EV_POS            33
+#define STM_ISR_I2C2_ER_POS            34
+#define STM_ISR_SPI1_POS               35
+#define STM_ISR_SPI2_POS               36
+#define STM_ISR_USART1_POS             37
+#define STM_ISR_USART2_POS             38
+#define STM_ISR_USART3_POS             39
+#define STM_ISR_EXTI15_10_POS          40
+#define STM_ISR_RTC_ALARM_POS          41
+#define STM_ISR_USB_FS_WKUP_POS                42
+#define STM_ISR_TIM6_POS               43
+#define STM_ISR_TIM7_POS               44
+
+struct stm_syscfg {
+       vuint32_t       memrmp;
+       vuint32_t       pmc;
+       vuint32_t       exticr[4];
+};
+
+extern struct stm_syscfg stm_syscfg;
+
+#define STM_SYSCFG_MEMRMP_MEM_MODE     0
+#define  STM_SYSCFG_MEMRMP_MEM_MODE_MAIN_FLASH         0
+#define  STM_SYSCFG_MEMRMP_MEM_MODE_SYSTEM_FLASH       1
+#define  STM_SYSCFG_MEMRMP_MEM_MODE_SRAM               3
+#define  STM_SYSCFG_MEMRMP_MEM_MODE_MASK               3
+
+#define STM_SYSCFG_PMC_USB_PU          0
+
+#define STM_SYSCFG_EXTICR_PA           0
+#define STM_SYSCFG_EXTICR_PB           1
+#define STM_SYSCFG_EXTICR_PC           2
+#define STM_SYSCFG_EXTICR_PD           3
+#define STM_SYSCFG_EXTICR_PE           4
+#define STM_SYSCFG_EXTICR_PH           5
+
+static inline void
+stm_exticr_set(struct stm_gpio *gpio, int pin) {
+       uint8_t reg = pin >> 2;
+       uint8_t shift = (pin & 3) << 2;
+       uint8_t val = 0;
+
+       /* Enable SYSCFG */
+       stm_rcc.apb2enr |= (1 << STM_RCC_APB2ENR_SYSCFGEN);
+
+       if (gpio == &stm_gpioa)
+               val = STM_SYSCFG_EXTICR_PA;
+       else if (gpio == &stm_gpiob)
+               val = STM_SYSCFG_EXTICR_PB;
+       else if (gpio == &stm_gpioc)
+               val = STM_SYSCFG_EXTICR_PC;
+       else if (gpio == &stm_gpiod)
+               val = STM_SYSCFG_EXTICR_PD;
+       else if (gpio == &stm_gpioe)
+               val = STM_SYSCFG_EXTICR_PE;
+
+       stm_syscfg.exticr[reg] = (stm_syscfg.exticr[reg] & ~(0xf << shift)) | val << shift;
+}
+
+
+struct stm_dma_channel {
+       vuint32_t       ccr;
+       vuint32_t       cndtr;
+       vvoid_t         cpar;
+       vvoid_t         cmar;
+       vuint32_t       reserved;
+};
+
+#define STM_NUM_DMA    7
+
+struct stm_dma {
+       vuint32_t               isr;
+       vuint32_t               ifcr;
+       struct stm_dma_channel  channel[STM_NUM_DMA];
+};
+
+extern struct stm_dma stm_dma;
+
+/* DMA channels go from 1 to 7, instead of 0 to 6 (sigh)
+ */
+
+#define STM_DMA_INDEX(channel)         ((channel) - 1)
+
+#define STM_DMA_ISR(index)             ((index) << 2)
+#define STM_DMA_ISR_MASK                       0xf
+#define STM_DMA_ISR_TEIF                       3
+#define STM_DMA_ISR_HTIF                       2
+#define STM_DMA_ISR_TCIF                       1
+#define STM_DMA_ISR_GIF                                0
+
+#define STM_DMA_IFCR(index)            ((index) << 2)
+#define STM_DMA_IFCR_MASK                      0xf
+#define STM_DMA_IFCR_CTEIF                     3
+#define STM_DMA_IFCR_CHTIF                     2
+#define STM_DMA_IFCR_CTCIF                     1
+#define STM_DMA_IFCR_CGIF                      0
+
+#define STM_DMA_CCR_MEM2MEM            (14)
+
+#define STM_DMA_CCR_PL                 (12)
+#define  STM_DMA_CCR_PL_LOW                    (0)
+#define  STM_DMA_CCR_PL_MEDIUM                 (1)
+#define  STM_DMA_CCR_PL_HIGH                   (2)
+#define  STM_DMA_CCR_PL_VERY_HIGH              (3)
+#define  STM_DMA_CCR_PL_MASK                   (3)
+
+#define STM_DMA_CCR_MSIZE              (10)
+#define  STM_DMA_CCR_MSIZE_8                   (0)
+#define  STM_DMA_CCR_MSIZE_16                  (1)
+#define  STM_DMA_CCR_MSIZE_32                  (2)
+#define  STM_DMA_CCR_MSIZE_MASK                        (3)
+
+#define STM_DMA_CCR_PSIZE              (8)
+#define  STM_DMA_CCR_PSIZE_8                   (0)
+#define  STM_DMA_CCR_PSIZE_16                  (1)
+#define  STM_DMA_CCR_PSIZE_32                  (2)
+#define  STM_DMA_CCR_PSIZE_MASK                        (3)
+
+#define STM_DMA_CCR_MINC               (7)
+#define STM_DMA_CCR_PINC               (6)
+#define STM_DMA_CCR_CIRC               (5)
+#define STM_DMA_CCR_DIR                        (4)
+#define  STM_DMA_CCR_DIR_PER_TO_MEM            0
+#define  STM_DMA_CCR_DIR_MEM_TO_PER            1
+#define STM_DMA_CCR_TEIE               (3)
+#define STM_DMA_CCR_HTIE               (2)
+#define STM_DMA_CCR_TCIE               (1)
+#define STM_DMA_CCR_EN                 (0)
+
+#define STM_DMA_CHANNEL_ADC1           1
+#define STM_DMA_CHANNEL_SPI1_RX                2
+#define STM_DMA_CHANNEL_SPI1_TX                3
+#define STM_DMA_CHANNEL_SPI2_RX                4
+#define STM_DMA_CHANNEL_SPI2_TX                5
+#define STM_DMA_CHANNEL_USART3_TX      2
+#define STM_DMA_CHANNEL_USART3_RX      3
+#define STM_DMA_CHANNEL_USART1_TX      4
+#define STM_DMA_CHANNEL_USART1_RX      5
+#define STM_DMA_CHANNEL_USART2_RX      6
+#define STM_DMA_CHANNEL_USART2_TX      7
+#define STM_DMA_CHANNEL_I2C2_TX                4
+#define STM_DMA_CHANNEL_I2C2_RX                5
+#define STM_DMA_CHANNEL_I2C1_TX                6
+#define STM_DMA_CHANNEL_I2C1_RX                7
+#define STM_DMA_CHANNEL_TIM2_CH3       1
+#define STM_DMA_CHANNEL_TIM2_UP                2
+#define STM_DMA_CHANNEL_TIM2_CH1       5
+#define STM_DMA_CHANNEL_TIM2_CH2       7
+#define STM_DMA_CHANNEL_TIM2_CH4       7
+#define STM_DMA_CHANNEL_TIM3_CH3       2
+#define STM_DMA_CHANNEL_TIM3_CH4       3
+#define STM_DMA_CHANNEL_TIM3_UP                3
+#define STM_DMA_CHANNEL_TIM3_CH1       6
+#define STM_DMA_CHANNEL_TIM3_TRIG      6
+#define STM_DMA_CHANNEL_TIM4_CH1       1
+#define STM_DMA_CHANNEL_TIM4_CH2       4
+#define STM_DMA_CHANNEL_TIM4_CH3       5
+#define STM_DMA_CHANNEL_TIM4_UP                7
+#define STM_DMA_CHANNEL_TIM6_UP_DA     2
+#define STM_DMA_CHANNEL_C_CHANNEL1     2
+#define STM_DMA_CHANNEL_TIM7_UP_DA     3
+#define STM_DMA_CHANNEL_C_CHANNEL2     3
+
+/*
+ * Only spi channel 1 and 2 can use DMA
+ */
+#define STM_NUM_SPI    2
+
+struct stm_spi {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       sr;
+       vuint32_t       dr;
+       vuint32_t       crcpr;
+       vuint32_t       rxcrcr;
+       vuint32_t       txcrcr;
+};
+
+extern struct stm_spi stm_spi1, stm_spi2, stm_spi3;
+
+/* SPI channels go from 1 to 3, instead of 0 to 2 (sigh)
+ */
+
+#define STM_SPI_INDEX(channel)         ((channel) - 1)
+
+#define STM_SPI_CR1_BIDIMODE           15
+#define STM_SPI_CR1_BIDIOE             14
+#define STM_SPI_CR1_CRCEN              13
+#define STM_SPI_CR1_CRCNEXT            12
+#define STM_SPI_CR1_DFF                        11
+#define STM_SPI_CR1_RXONLY             10
+#define STM_SPI_CR1_SSM                        9
+#define STM_SPI_CR1_SSI                        8
+#define STM_SPI_CR1_LSBFIRST           7
+#define STM_SPI_CR1_SPE                        6
+#define STM_SPI_CR1_BR                 3
+#define  STM_SPI_CR1_BR_PCLK_2                 0
+#define  STM_SPI_CR1_BR_PCLK_4                 1
+#define  STM_SPI_CR1_BR_PCLK_8                 2
+#define  STM_SPI_CR1_BR_PCLK_16                        3
+#define  STM_SPI_CR1_BR_PCLK_32                        4
+#define  STM_SPI_CR1_BR_PCLK_64                        5
+#define  STM_SPI_CR1_BR_PCLK_128               6
+#define  STM_SPI_CR1_BR_PCLK_256               7
+#define  STM_SPI_CR1_BR_MASK                   7
+
+#define STM_SPI_CR1_MSTR               2
+#define STM_SPI_CR1_CPOL               1
+#define STM_SPI_CR1_CPHA               0
+
+#define STM_SPI_CR2_TXEIE      7
+#define STM_SPI_CR2_RXNEIE     6
+#define STM_SPI_CR2_ERRIE      5
+#define STM_SPI_CR2_SSOE       2
+#define STM_SPI_CR2_TXDMAEN    1
+#define STM_SPI_CR2_RXDMAEN    0
+
+#define STM_SPI_SR_BSY         7
+#define STM_SPI_SR_OVR         6
+#define STM_SPI_SR_MODF                5
+#define STM_SPI_SR_CRCERR      4
+#define STM_SPI_SR_TXE         1
+#define STM_SPI_SR_RXNE                0
+
+struct stm_adc {
+       vuint32_t       sr;
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smpr1;
+       vuint32_t       smpr2;
+       vuint32_t       smpr3;
+       vuint32_t       jofr1;
+       vuint32_t       jofr2;
+       vuint32_t       jofr3;
+       vuint32_t       jofr4;
+       vuint32_t       htr;
+       vuint32_t       ltr;
+       vuint32_t       sqr1;
+       vuint32_t       sqr2;
+       vuint32_t       sqr3;
+       vuint32_t       sqr4;
+       vuint32_t       sqr5;
+       vuint32_t       jsqr;
+       vuint32_t       jdr1;
+       vuint32_t       jdr2;
+       vuint32_t       jdr3;
+       vuint32_t       jdr4;
+       vuint32_t       dr;
+       uint8_t         reserved[0x300 - 0x5c];
+       vuint32_t       csr;
+       vuint32_t       ccr;
+};
+
+extern struct stm_adc stm_adc;
+
+#define STM_ADC_SR_JCNR                9
+#define STM_ADC_SR_RCNR                8
+#define STM_ADC_SR_ADONS       6
+#define STM_ADC_SR_OVR         5
+#define STM_ADC_SR_STRT                4
+#define STM_ADC_SR_JSTRT       3
+#define STM_ADC_SR_JEOC                2
+#define STM_ADC_SR_EOC         1
+#define STM_ADC_SR_AWD         0
+
+#define STM_ADC_CR1_OVRIE      26
+#define STM_ADC_CR1_RES                24
+#define  STM_ADC_CR1_RES_12            0
+#define  STM_ADC_CR1_RES_10            1
+#define  STM_ADC_CR1_RES_8             2
+#define  STM_ADC_CR1_RES_6             3
+#define  STM_ADC_CR1_RES_MASK          3
+#define STM_ADC_CR1_AWDEN       23
+#define STM_ADC_CR1_JAWDEN     22
+#define STM_ADC_CR1_PDI                17
+#define STM_ADC_CR1_PDD                16
+#define STM_ADC_CR1_DISCNUM    13
+#define  STM_ADC_CR1_DISCNUM_1         0
+#define  STM_ADC_CR1_DISCNUM_2         1
+#define  STM_ADC_CR1_DISCNUM_3         2
+#define  STM_ADC_CR1_DISCNUM_4         3
+#define  STM_ADC_CR1_DISCNUM_5         4
+#define  STM_ADC_CR1_DISCNUM_6         5
+#define  STM_ADC_CR1_DISCNUM_7         6
+#define  STM_ADC_CR1_DISCNUM_8         7
+#define  STM_ADC_CR1_DISCNUM_MASK      7
+#define STM_ADC_CR1_JDISCEN    12
+#define STM_ADC_CR1_DISCEN     11
+#define STM_ADC_CR1_JAUTO      10
+#define STM_ADC_CR1_AWDSGL     9
+#define STM_ADC_CR1_SCAN       8
+#define STM_ADC_CR1_JEOCIE     7
+#define STM_ADC_CR1_AWDIE      6
+#define STM_ADC_CR1_EOCIE      5
+#define STM_ADC_CR1_AWDCH      0
+#define  STM_ADC_CR1_AWDCH_MASK                0x1f
+
+#define STM_ADC_CR2_SWSTART    30
+#define STM_ADC_CR2_EXTEN      28
+#define  STM_ADC_CR2_EXTEN_DISABLE     0
+#define  STM_ADC_CR2_EXTEN_RISING      1
+#define  STM_ADC_CR2_EXTEN_FALLING     2
+#define  STM_ADC_CR2_EXTEN_BOTH                3
+#define  STM_ADC_CR2_EXTEN_MASK                3
+#define STM_ADC_CR2_EXTSEL     24
+#define  STM_ADC_CR2_EXTSEL_TIM9_CC2   0
+#define  STM_ADC_CR2_EXTSEL_TIM9_TRGO  1
+#define  STM_ADC_CR2_EXTSEL_TIM2_CC3   2
+#define  STM_ADC_CR2_EXTSEL_TIM2_CC2   3
+#define  STM_ADC_CR2_EXTSEL_TIM3_TRGO  4
+#define  STM_ADC_CR2_EXTSEL_TIM4_CC4   5
+#define  STM_ADC_CR2_EXTSEL_TIM2_TRGO  6
+#define  STM_ADC_CR2_EXTSEL_TIM3_CC1   7
+#define  STM_ADC_CR2_EXTSEL_TIM3_CC3   8
+#define  STM_ADC_CR2_EXTSEL_TIM4_TRGO  9
+#define  STM_ADC_CR2_EXTSEL_TIM6_TRGO  10
+#define  STM_ADC_CR2_EXTSEL_EXTI_11    15
+#define  STM_ADC_CR2_EXTSEL_MASK       15
+#define STM_ADC_CR2_JWSTART    22
+#define STM_ADC_CR2_JEXTEN     20
+#define  STM_ADC_CR2_JEXTEN_DISABLE    0
+#define  STM_ADC_CR2_JEXTEN_RISING     1
+#define  STM_ADC_CR2_JEXTEN_FALLING    2
+#define  STM_ADC_CR2_JEXTEN_BOTH       3
+#define  STM_ADC_CR2_JEXTEN_MASK       3
+#define STM_ADC_CR2_JEXTSEL    16
+#define  STM_ADC_CR2_JEXTSEL_TIM9_CC1  0
+#define  STM_ADC_CR2_JEXTSEL_TIM9_TRGO 1
+#define  STM_ADC_CR2_JEXTSEL_TIM2_TRGO 2
+#define  STM_ADC_CR2_JEXTSEL_TIM2_CC1  3
+#define  STM_ADC_CR2_JEXTSEL_TIM3_CC4  4
+#define  STM_ADC_CR2_JEXTSEL_TIM4_TRGO 5
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC1  6
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC2  7
+#define  STM_ADC_CR2_JEXTSEL_TIM4_CC3  8
+#define  STM_ADC_CR2_JEXTSEL_TIM10_CC1 9
+#define  STM_ADC_CR2_JEXTSEL_TIM7_TRGO 10
+#define  STM_ADC_CR2_JEXTSEL_EXTI_15   15
+#define  STM_ADC_CR2_JEXTSEL_MASK      15
+#define STM_ADC_CR2_ALIGN      11
+#define STM_ADC_CR2_EOCS       10
+#define STM_ADC_CR2_DDS                9
+#define STM_ADC_CR2_DMA                8
+#define STM_ADC_CR2_DELS       4
+#define  STM_ADC_CR2_DELS_NONE         0
+#define  STM_ADC_CR2_DELS_UNTIL_READ   1
+#define  STM_ADC_CR2_DELS_7            2
+#define  STM_ADC_CR2_DELS_15           3
+#define  STM_ADC_CR2_DELS_31           4
+#define  STM_ADC_CR2_DELS_63           5
+#define  STM_ADC_CR2_DELS_127          6
+#define  STM_ADC_CR2_DELS_255          7
+#define  STM_ADC_CR2_DELS_MASK         7
+#define STM_ADC_CR2_CONT       1
+#define STM_ADC_CR2_ADON       0
+
+#define STM_ADC_CCR_TSVREFE    23
+#define STM_ADC_CCR_ADCPRE     16
+#define  STM_ADC_CCR_ADCPRE_HSI_1      0
+#define  STM_ADC_CCR_ADCPRE_HSI_2      1
+#define  STM_ADC_CCR_ADCPRE_HSI_4      2
+#define  STM_ADC_CCR_ADCPRE_MASK       3
+
+struct stm_temp_cal {
+       uint16_t        vref;
+       uint16_t        ts_cal_cold;
+       uint16_t        reserved;
+       uint16_t        ts_cal_hot;
+};
+
+extern struct stm_temp_cal     stm_temp_cal;
+
+#define stm_temp_cal_cold      25
+#define stm_temp_cal_hot       110
+
+#define STM_NUM_I2C    2
+
+#define STM_I2C_INDEX(channel) ((channel) - 1)
+
+struct stm_i2c {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       oar1;
+       vuint32_t       oar2;
+       vuint32_t       dr;
+       vuint32_t       sr1;
+       vuint32_t       sr2;
+       vuint32_t       ccr;
+       vuint32_t       trise;
+};
+
+extern struct stm_i2c stm_i2c1, stm_i2c2;
+
+#define STM_I2C_CR1_SWRST      15
+#define STM_I2C_CR1_ALERT      13
+#define STM_I2C_CR1_PEC                12
+#define STM_I2C_CR1_POS                11
+#define STM_I2C_CR1_ACK                10
+#define STM_I2C_CR1_STOP       9
+#define STM_I2C_CR1_START      8
+#define STM_I2C_CR1_NOSTRETCH  7
+#define STM_I2C_CR1_ENGC       6
+#define STM_I2C_CR1_ENPEC      5
+#define STM_I2C_CR1_ENARP      4
+#define STM_I2C_CR1_SMBTYPE    3
+#define STM_I2C_CR1_SMBUS      1
+#define STM_I2C_CR1_PE         0
+
+#define STM_I2C_CR2_LAST       12
+#define STM_I2C_CR2_DMAEN      11
+#define STM_I2C_CR2_ITBUFEN    10
+#define STM_I2C_CR2_ITEVTEN    9
+#define STM_I2C_CR2_ITERREN    8
+#define STM_I2C_CR2_FREQ       0
+#define  STM_I2C_CR2_FREQ_2_MHZ                2
+#define  STM_I2C_CR2_FREQ_4_MHZ                4
+#define  STM_I2C_CR2_FREQ_8_MHZ                8
+#define  STM_I2C_CR2_FREQ_16_MHZ       16
+#define  STM_I2C_CR2_FREQ_32_MHZ       32
+#define  STM_I2C_CR2_FREQ_MASK         0x3f
+
+#define STM_I2C_SR1_SMBALERT   15
+#define STM_I2C_SR1_TIMEOUT    14
+#define STM_I2C_SR1_PECERR     12
+#define STM_I2C_SR1_OVR                11
+#define STM_I2C_SR1_AF         10
+#define STM_I2C_SR1_ARLO       9
+#define STM_I2C_SR1_BERR       8
+#define STM_I2C_SR1_TXE                7
+#define STM_I2C_SR1_RXNE       6
+#define STM_I2C_SR1_STOPF      4
+#define STM_I2C_SR1_ADD10      3
+#define STM_I2C_SR1_BTF                2
+#define STM_I2C_SR1_ADDR       1
+#define STM_I2C_SR1_SB         0
+
+#define STM_I2C_SR2_PEC                8
+#define  STM_I2C_SR2_PEC_MASK  0xff00
+#define STM_I2C_SR2_DUALF      7
+#define STM_I2C_SR2_SMBHOST    6
+#define STM_I2C_SR2_SMBDEFAULT 5
+#define STM_I2C_SR2_GENCALL    4
+#define STM_I2C_SR2_TRA                2
+#define STM_I2C_SR2_BUSY               1
+#define STM_I2C_SR2_MSL                0
+
+#define STM_I2C_CCR_FS         15
+#define STM_I2C_CCR_DUTY       14
+#define STM_I2C_CCR_CCR                0
+#define  STM_I2C_CCR_MASK      0x7ff
+
+struct stm_tim234 {
+       vuint32_t       cr1;
+       vuint32_t       cr2;
+       vuint32_t       smcr;
+       vuint32_t       dier;
+
+       vuint32_t       sr;
+       vuint32_t       egr;
+       vuint32_t       ccmr1;
+       vuint32_t       ccmr2;
+
+       vuint32_t       ccer;
+       vuint32_t       cnt;
+       vuint32_t       psc;
+       vuint32_t       arr;
+
+       uint32_t        reserved_30;
+       vuint32_t       ccr1;
+       vuint32_t       ccr2;
+       vuint32_t       ccr3;
+
+       vuint32_t       ccr4;
+       uint32_t        reserved_44;
+       vuint32_t       dcr;
+       vuint32_t       dmar;
+
+       uint32_t        reserved_50;
+};
+
+extern struct stm_tim234 stm_tim2, stm_tim3, stm_tim4;
+
+#define STM_TIM234_CR1_CKD     8
+#define  STM_TIM234_CR1_CKD_1          0
+#define  STM_TIM234_CR1_CKD_2          1
+#define  STM_TIM234_CR1_CKD_4          2
+#define  STM_TIM234_CR1_CKD_MASK       3
+#define STM_TIM234_CR1_ARPE    7
+#define STM_TIM234_CR1_CMS     5
+#define  STM_TIM234_CR1_CMS_EDGE       0
+#define  STM_TIM234_CR1_CMS_CENTER_1   1
+#define  STM_TIM234_CR1_CMS_CENTER_2   2
+#define  STM_TIM234_CR1_CMS_CENTER_3   3
+#define  STM_TIM234_CR1_CMS_MASK       3
+#define STM_TIM234_CR1_DIR     4
+#define  STM_TIM234_CR1_DIR_UP         0
+#define  STM_TIM234_CR1_DIR_DOWN       1
+#define STM_TIM234_CR1_OPM     3
+#define STM_TIM234_CR1_URS     2
+#define STM_TIM234_CR1_UDIS    1
+#define STM_TIM234_CR1_CEN     0
+
+#define STM_TIM234_CR2_TI1S    7
+#define STM_TIM234_CR2_MMS     4
+#define  STM_TIM234_CR2_MMS_RESET              0
+#define  STM_TIM234_CR2_MMS_ENABLE             1
+#define  STM_TIM234_CR2_MMS_UPDATE             2
+#define  STM_TIM234_CR2_MMS_COMPARE_PULSE      3
+#define  STM_TIM234_CR2_MMS_COMPARE_OC1REF     4
+#define  STM_TIM234_CR2_MMS_COMPARE_OC2REF     5
+#define  STM_TIM234_CR2_MMS_COMPARE_OC3REF     6
+#define  STM_TIM234_CR2_MMS_COMPARE_OC4REF     7
+#define  STM_TIM234_CR2_MMS_MASK               7
+#define STM_TIM234_CR2_CCDS    3
+
+#define STM_TIM234_SMCR_ETP    15
+#define STM_TIM234_SMCR_ECE    14
+#define STM_TIM234_SMCR_ETPS   12
+#define  STM_TIM234_SMCR_ETPS_OFF              0
+#define  STM_TIM234_SMCR_ETPS_DIV_2            1
+#define  STM_TIM234_SMCR_ETPS_DIV_4            2
+#define  STM_TIM234_SMCR_ETPS_DIV_8            3
+#define  STM_TIM234_SMCR_ETPS_MASK             3
+#define STM_TIM234_SMCR_ETF    8
+#define  STM_TIM234_SMCR_ETF_NONE              0
+#define  STM_TIM234_SMCR_ETF_INT_N_2           1
+#define  STM_TIM234_SMCR_ETF_INT_N_4           2
+#define  STM_TIM234_SMCR_ETF_INT_N_8           3
+#define  STM_TIM234_SMCR_ETF_DTS_2_N_6         4
+#define  STM_TIM234_SMCR_ETF_DTS_2_N_8         5
+#define  STM_TIM234_SMCR_ETF_DTS_4_N_6         6
+#define  STM_TIM234_SMCR_ETF_DTS_4_N_8         7
+#define  STM_TIM234_SMCR_ETF_DTS_8_N_6         8
+#define  STM_TIM234_SMCR_ETF_DTS_8_N_8         9
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_5                10
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_6                11
+#define  STM_TIM234_SMCR_ETF_DTS_16_N_8                12
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_5                13
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_6                14
+#define  STM_TIM234_SMCR_ETF_DTS_32_N_8                15
+#define  STM_TIM234_SMCR_ETF_MASK              15
+#define STM_TIM234_SMCR_MSM    7
+#define STM_TIM234_SMCR_TS     4
+#define  STM_TIM234_SMCR_TS_ITR0               0
+#define  STM_TIM234_SMCR_TS_ITR1               1
+#define  STM_TIM234_SMCR_TS_ITR2               2
+#define  STM_TIM234_SMCR_TS_ITR3               3
+#define  STM_TIM234_SMCR_TS_TI1F_ED            4
+#define  STM_TIM234_SMCR_TS_TI1FP1             5
+#define  STM_TIM234_SMCR_TS_TI2FP2             6
+#define  STM_TIM234_SMCR_TS_ETRF               7
+#define  STM_TIM234_SMCR_TS_MASK               7
+#define STM_TIM234_SMCR_OCCS   3
+#define STM_TIM234_SMCR_SMS    0
+#define  STM_TIM234_SMCR_SMS_DISABLE           0
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_1    1
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_2    2
+#define  STM_TIM234_SMCR_SMS_ENCODER_MODE_3    3
+#define  STM_TIM234_SMCR_SMS_RESET_MODE                4
+#define  STM_TIM234_SMCR_SMS_GATED_MODE                5
+#define  STM_TIM234_SMCR_SMS_TRIGGER_MODE      6
+#define  STM_TIM234_SMCR_SMS_EXTERNAL_CLOCK    7
+#define  STM_TIM234_SMCR_SMS_MASK              7
+
+#define STM_TIM234_SR_CC4OF    12
+#define STM_TIM234_SR_CC3OF    11
+#define STM_TIM234_SR_CC2OF    10
+#define STM_TIM234_SR_CC1OF    9
+#define STM_TIM234_SR_TIF      6
+#define STM_TIM234_SR_CC4IF    4
+#define STM_TIM234_SR_CC3IF    3
+#define STM_TIM234_SR_CC2IF    2
+#define STM_TIM234_SR_CC1IF    1
+#define STM_TIM234_SR_UIF      0
+
+#define STM_TIM234_CCMR1_OC2CE 15
+#define STM_TIM234_CCMR1_OC2M  12
+#define  STM_TIM234_CCMR1_OC2M_FROZEN                  0
+#define  STM_TIM234_CCMR1_OC2M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR1_OC2M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR1_OC2M_TOGGLE                  3
+#define  STM_TIM234_CCMR1_OC2M_FORCE_LOW               4
+#define  STM_TIM234_CCMR1_OC2M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR1_OC2M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR1_OC2M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR1_OC2M_MASK                    7
+#define STM_TIM234_CCMR1_OC2PE 11
+#define STM_TIM234_CCMR1_OC2FE 10
+#define STM_TIM234_CCMR1_CC2S  8
+#define  STM_TIM234_CCMR1_CC2S_OUTPUT                  0
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TI2               1
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TI1               2
+#define  STM_TIM234_CCMR1_CC2S_INPUT_TRC               3
+#define  STM_TIM234_CCMR1_CC2S_MASK                    3
+
+#define STM_TIM234_CCMR1_OC1CE 7
+#define STM_TIM234_CCMR1_OC1M  4
+#define  STM_TIM234_CCMR1_OC1M_FROZEN                  0
+#define  STM_TIM234_CCMR1_OC1M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR1_OC1M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR1_OC1M_TOGGLE                  3
+#define  STM_TIM234_CCMR1_OC1M_FORCE_LOW               4
+#define  STM_TIM234_CCMR1_OC1M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR1_OC1M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR1_OC1M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR1_OC1M_MASK                    7
+#define STM_TIM234_CCMR1_OC1PE 11
+#define STM_TIM234_CCMR1_OC1FE 2
+#define STM_TIM234_CCMR1_CC1S  0
+#define  STM_TIM234_CCMR1_CC1S_OUTPUT                  0
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TI1               1
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TI2               2
+#define  STM_TIM234_CCMR1_CC1S_INPUT_TRC               3
+#define  STM_TIM234_CCMR1_CC1S_MASK                    3
+
+#define STM_TIM234_CCMR2_OC2CE 15
+#define STM_TIM234_CCMR2_OC4M  12
+#define  STM_TIM234_CCMR2_OC4M_FROZEN                  0
+#define  STM_TIM234_CCMR2_OC4M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR2_OC4M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR2_OC4M_TOGGLE                  3
+#define  STM_TIM234_CCMR2_OC4M_FORCE_LOW               4
+#define  STM_TIM234_CCMR2_OC4M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR2_OC4M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR2_OC4M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR2_OC4M_MASK                    7
+#define STM_TIM234_CCMR2_OC4PE 11
+#define STM_TIM234_CCMR2_OC4FE 10
+#define STM_TIM234_CCMR2_CC4S  8
+#define  STM_TIM234_CCMR2_CC4S_OUTPUT                  0
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TI4               1
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TI3               2
+#define  STM_TIM234_CCMR2_CC4S_INPUT_TRC               3
+#define  STM_TIM234_CCMR2_CC4S_MASK                    3
+
+#define STM_TIM234_CCMR2_OC3CE 7
+#define STM_TIM234_CCMR2_OC3M  4
+#define  STM_TIM234_CCMR2_OC3M_FROZEN                  0
+#define  STM_TIM234_CCMR2_OC3M_SET_HIGH_ON_MATCH       1
+#define  STM_TIM234_CCMR2_OC3M_SET_LOW_ON_MATCH                2
+#define  STM_TIM234_CCMR2_OC3M_TOGGLE                  3
+#define  STM_TIM234_CCMR2_OC3M_FORCE_LOW               4
+#define  STM_TIM234_CCMR2_OC3M_FORCE_HIGH              5
+#define  STM_TIM234_CCMR2_OC3M_PWM_MODE_1              6
+#define  STM_TIM234_CCMR2_OC3M_PWM_MODE_2              7
+#define  STM_TIM234_CCMR2_OC3M_MASK                    7
+#define STM_TIM234_CCMR2_OC3PE 11
+#define STM_TIM234_CCMR2_OC3FE 2
+#define STM_TIM234_CCMR2_CC3S  0
+#define  STM_TIM234_CCMR2_CC3S_OUTPUT                  0
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TI3               1
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TI4               2
+#define  STM_TIM234_CCMR2_CC3S_INPUT_TRC               3
+#define  STM_TIM234_CCMR2_CC3S_MASK                    3
+
+#define STM_TIM234_CCER_CC4NP  15
+#define STM_TIM234_CCER_CC4P   13
+#define STM_TIM234_CCER_CC4E   12
+#define STM_TIM234_CCER_CC3NP  11
+#define STM_TIM234_CCER_CC3P   9
+#define STM_TIM234_CCER_CC3E   8
+#define STM_TIM234_CCER_CC2NP  7
+#define STM_TIM234_CCER_CC2P   5
+#define STM_TIM234_CCER_CC2E   4
+#define STM_TIM234_CCER_CC1NP  3
+#define STM_TIM234_CCER_CC1P   1
+#define STM_TIM234_CCER_CC1E   0
+
+struct stm_usb {
+       vuint32_t       epr[8];
+       uint8_t         reserved_20[0x40 - 0x20];
+       vuint32_t       cntr;
+       vuint32_t       istr;
+       vuint32_t       fnr;
+       vuint32_t       daddr;
+       vuint32_t       btable;
+};
+
+#define STM_USB_EPR_CTR_RX     15
+#define  STM_USB_EPR_CTR_RX_WRITE_INVARIANT            1
+#define STM_USB_EPR_DTOG_RX    14
+#define STM_USB_EPR_DTOG_RX_WRITE_INVARIANT            0
+#define STM_USB_EPR_STAT_RX    12
+#define  STM_USB_EPR_STAT_RX_DISABLED                  0
+#define  STM_USB_EPR_STAT_RX_STALL                     1
+#define  STM_USB_EPR_STAT_RX_NAK                       2
+#define  STM_USB_EPR_STAT_RX_VALID                     3
+#define  STM_USB_EPR_STAT_RX_MASK                      3
+#define  STM_USB_EPR_STAT_RX_WRITE_INVARIANT           0
+#define STM_USB_EPR_SETUP      11
+#define STM_USB_EPR_EP_TYPE    9
+#define  STM_USB_EPR_EP_TYPE_BULK                      0
+#define  STM_USB_EPR_EP_TYPE_CONTROL                   1
+#define  STM_USB_EPR_EP_TYPE_ISO                       2
+#define  STM_USB_EPR_EP_TYPE_INTERRUPT                 3
+#define  STM_USB_EPR_EP_TYPE_MASK                      3
+#define STM_USB_EPR_EP_KIND    8
+#define  STM_USB_EPR_EP_KIND_DBL_BUF                   1       /* Bulk */
+#define  STM_USB_EPR_EP_KIND_STATUS_OUT                        1       /* Control */
+#define STM_USB_EPR_CTR_TX     7
+#define  STM_USB_CTR_TX_WRITE_INVARIANT                        1
+#define STM_USB_EPR_DTOG_TX    6
+#define  STM_USB_EPR_DTOG_TX_WRITE_INVARIANT           0
+#define STM_USB_EPR_STAT_TX    4
+#define  STM_USB_EPR_STAT_TX_DISABLED                  0
+#define  STM_USB_EPR_STAT_TX_STALL                     1
+#define  STM_USB_EPR_STAT_TX_NAK                       2
+#define  STM_USB_EPR_STAT_TX_VALID                     3
+#define  STM_USB_EPR_STAT_TX_WRITE_INVARIANT           0
+#define  STM_USB_EPR_STAT_TX_MASK                      3
+#define STM_USB_EPR_EA         0
+#define  STM_USB_EPR_EA_MASK                           0xf
+
+#define STM_USB_CNTR_CTRM      15
+#define STM_USB_CNTR_PMAOVRM   14
+#define STM_USB_CNTR_ERRM      13
+#define STM_USB_CNTR_WKUPM     12
+#define STM_USB_CNTR_SUSPM     11
+#define STM_USB_CNTR_RESETM    10
+#define STM_USB_CNTR_SOFM      9
+#define STM_USB_CNTR_ESOFM     8
+#define STM_USB_CNTR_RESUME    4
+#define STM_USB_CNTR_FSUSP     3
+#define STM_USB_CNTR_LP_MODE   2
+#define STM_USB_CNTR_PDWN      1
+#define STM_USB_CNTR_FRES      0
+
+#define STM_USB_ISTR_CTR       15
+#define STM_USB_ISTR_PMAOVR    14
+#define STM_USB_ISTR_ERR       13
+#define STM_USB_ISTR_WKUP      12
+#define STM_USB_ISTR_SUSP      11
+#define STM_USB_ISTR_RESET     10
+#define STM_USB_ISTR_SOF       9
+#define STM_USB_ISTR_ESOF      8
+#define STM_USB_ISTR_DIR       4
+#define STM_USB_ISTR_EP_ID     0
+#define  STM_USB_ISTR_EP_ID_MASK               0xf
+
+#define STM_USB_FNR_RXDP       15
+#define STM_USB_FNR_RXDM       14
+#define STM_USB_FNR_LCK                13
+#define STM_USB_FNR_LSOF       11
+#define  STM_USB_FNR_LSOF_MASK                 0x3
+#define STM_USB_FNR_FN         0
+#define  STM_USB_FNR_FN_MASK                   0x7ff
+
+#define STM_USB_DADDR_EF       7
+#define STM_USB_DADDR_ADD      0
+#define  STM_USB_DADDR_ADD_MASK                        0x7f
+
+extern struct stm_usb stm_usb;
+
+union stm_usb_bdt {
+       struct {
+               vuint32_t       addr_tx;
+               vuint32_t       count_tx;
+               vuint32_t       addr_rx;
+               vuint32_t       count_rx;
+       } single;
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_tx[2];
+       struct {
+               vuint32_t       addr;
+               vuint32_t       count;
+       } double_rx[2];
+};
+
+#define STM_USB_BDT_COUNT_RX_BL_SIZE   15
+#define STM_USB_BDT_COUNT_RX_NUM_BLOCK 10
+#define  STM_USB_BDT_COUNT_RX_NUM_BLOCK_MASK   0x1f
+#define STM_USB_BDT_COUNT_RX_COUNT_RX  0
+#define  STM_USB_BDT_COUNT_RX_COUNT_RX_MASK    0x1ff
+
+#define STM_USB_BDT_SIZE       8
+
+extern uint8_t stm_usb_sram[];
+
+struct stm_exti {
+       vuint32_t       imr;
+       vuint32_t       emr;
+       vuint32_t       rtsr;
+       vuint32_t       ftsr;
+
+       vuint32_t       swier;
+       vuint32_t       pr;
+};
+
+extern struct stm_exti stm_exti;
+
+#endif /* _STM32L_H_ */
diff --git a/src/teleballoon-v1.1/.gitignore b/src/teleballoon-v1.1/.gitignore
new file mode 100644 (file)
index 0000000..21b236c
--- /dev/null
@@ -0,0 +1,2 @@
+teleballoon-*
+ao_product.h
diff --git a/src/teleballoon-v1.1/Makefile b/src/teleballoon-v1.1/Makefile
new file mode 100644 (file)
index 0000000..2eea996
--- /dev/null
@@ -0,0 +1,119 @@
+#
+# TeleBalloon build file
+#
+# The various telemetrum versions differ only
+# in which flash and GPS drivers are included,
+# so the per-board makefiles simply define
+# TM_VER, TM_DEF, TM_INC and TM_SRC and include
+# this file
+
+TELEBALLOON_VER=1.1
+TELEBALLOON_DEF=1_1
+
+TELEBALLOON_INC =
+
+TELEBALLOON_SRC = \
+       ao_companion.c \
+       ao_gps_skytraq.c \
+       ao_m25.c
+
+vpath %.c ..:../core:../cc1111:../drivers:../product:.
+vpath %.h ..:../core:../cc1111:../drivers:../product:.
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       cc1111.h \
+       altitude.h \
+       ao_kalman.h \
+       ao_product.h \
+       $(TELEBALLOON_INC)
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_convert.c \
+       ao_gps_report.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_task.c \
+       ao_balloon.c \
+       ao_sample.c \
+       ao_kalman.c \
+       ao_log.c \
+       ao_log_big.c \
+       ao_report.c \
+       ao_telemetry.c
+
+CC1111_SRC = \
+       ao_adc.c \
+       ao_beep.c \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_slave.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_string.c \
+       ao_spi.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC = \
+       $(TELEBALLOON_SRC)
+
+PRODUCT_SRC = \
+       ao_teleballoon.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = teleballoon-v$(TELEBALLOON_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleBalloon-v$(TELEBALLOON_VER)
+PRODUCT_DEF=-DTELEBALLOON_V_$(TELEBALLOON_DEF)
+IDPRODUCT=0x000b
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
+
diff --git a/src/teleballoon-v1.1/ao_balloon.c b/src/teleballoon-v1.1/ao_balloon.c
new file mode 100644 (file)
index 0000000..e897265
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef AO_FLIGHT_TEST
+#include "ao.h"
+#endif
+
+#ifndef HAS_ACCEL
+#error Please define HAS_ACCEL
+#endif
+
+#ifndef HAS_GPS
+#error Please define HAS_GPS
+#endif
+
+#ifndef HAS_USB
+#error Please define HAS_USB
+#endif
+
+/* Main flight thread. */
+
+__pdata enum ao_flight_state   ao_flight_state;        /* current flight state */
+
+__pdata uint8_t                        ao_flight_force_idle;
+
+void
+ao_flight(void)
+{
+       ao_sample_init();
+       ao_flight_state = ao_flight_startup;
+       for (;;) {
+
+               /*
+                * Process ADC samples, just looping
+                * until the sensors are calibrated.
+                */
+               if (!ao_sample())
+                       continue;
+
+               switch (ao_flight_state) {
+               case ao_flight_startup:
+
+                       /* Check to see what mode we should go to.
+                        *  - Invalid mode if accel cal appears to be out
+                        *  - pad mode if we're upright,
+                        *  - idle mode otherwise
+                        */
+                       if (!ao_flight_force_idle)
+                       {
+                               /* Set pad mode - we can fly! */
+                               ao_flight_state = ao_flight_pad;
+#if HAS_USB
+                               /* Disable the USB controller in flight mode
+                                * to save power
+                                */
+                               ao_usb_disable();
+#endif
+
+                               /* Disable packet mode in pad state */
+                               ao_packet_slave_stop();
+
+                               /* Turn on telemetry system */
+                               ao_rdf_set(1);
+                               ao_telemetry_set_interval(AO_TELEMETRY_INTERVAL_BALLOON);
+
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+                       } else {
+                               /* Set idle mode */
+                               ao_flight_state = ao_flight_idle;
+                               /* signal successful initialization by turning off the LED */
+                               ao_led_off(AO_LED_RED);
+                       }
+                       /* wakeup threads due to state change */
+                       ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+
+                       break;
+               case ao_flight_pad:
+
+                       /* pad to coast:
+                        *
+                        * barometer: > 20m vertical motion
+                        */
+                       if (ao_height > AO_M_TO_HEIGHT(20))
+                       {
+                               ao_flight_state = ao_flight_drogue;
+
+                               /* start logging data */
+                               ao_log_start();
+
+#if HAS_GPS
+                               /* Record current GPS position by waking up GPS log tasks */
+                               ao_wakeup(&ao_gps_data);
+                               ao_wakeup(&ao_gps_tracking_data);
+#endif
+
+                               ao_wakeup(DATA_TO_XDATA(&ao_flight_state));
+                       }
+                       break;
+               case ao_flight_drogue:
+                       break;
+                       
+               }
+       }
+}
+
+static __xdata struct ao_task  flight_task;
+
+void
+ao_flight_init(void)
+{
+       ao_flight_state = ao_flight_startup;
+       ao_add_task(&flight_task, ao_flight, "flight");
+}
diff --git a/src/teleballoon-v1.1/ao_pins.h b/src/teleballoon-v1.1/ao_pins.h
new file mode 100644 (file)
index 0000000..3305719
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#if defined(TELEBALLOON_V_1_1)
+
+       #define AO_SENSOR_INTERVAL_ASCENT       AO_MS_TO_TICKS(1000)
+       #define AO_SENSOR_INTERVAL_DESCENT      AO_MS_TO_TICKS(1000)
+       #define AO_OTHER_INTERVAL               AO_MS_TO_TICKS(1000)
+       #define AO_TELEMETRY_INTERVAL_BALLOON   AO_MS_TO_TICKS(1000)
+
+       #define HAS_FLIGHT              1
+       #define HAS_USB                 1
+       #define HAS_BEEP                1
+       #define HAS_GPS                 1
+       #define HAS_SERIAL_1            1
+       #define HAS_ADC                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define HAS_DBG                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            1
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+
+       #define HAS_COMPANION           1
+       #define COMPANION_CS_ON_P1      1
+       #define COMPANION_CS_MASK       0x4     /* CS1 is P1_2 */
+       #define COMPANION_CS            P1_2
+
+       #define AO_LED_RED              1
+       #define LEDS_AVAILABLE          (AO_LED_RED)
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL_REF           1
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define M25_CS_MASK             0x02    /* CS0 is P1_1 */
+       #define M25_MAX_CHIPS           1
+       #define HAS_ACCEL               1
+       #define HAS_IGNITE              0
+       #define HAS_MONITOR             0
+#endif
+
+#if DBG_ON_P1
+
+       #define DBG_CLOCK       (1 << 4)        /* mi0 */
+       #define DBG_DATA        (1 << 5)        /* mo0 */
+       #define DBG_RESET_N     (1 << 3)        /* c0 */
+
+       #define DBG_CLOCK_PIN   (P1_4)
+       #define DBG_DATA_PIN    (P1_5)
+       #define DBG_RESET_N_PIN (P1_3)
+
+       #define DBG_PORT_NUM    1
+       #define DBG_PORT        P1
+       #define DBG_PORT_SEL    P1SEL
+       #define DBG_PORT_INP    P1INP
+       #define DBG_PORT_DIR    P1DIR
+
+#endif /* DBG_ON_P1 */
+
+#if DBG_ON_P0
+
+       #define DBG_CLOCK       (1 << 3)
+       #define DBG_DATA        (1 << 4)
+       #define DBG_RESET_N     (1 << 5)
+
+       #define DBG_CLOCK_PIN   (P0_3)
+       #define DBG_DATA_PIN    (P0_4)
+       #define DBG_RESET_N_PIN (P0_5)
+
+       #define DBG_PORT_NUM    0
+       #define DBG_PORT        P0
+       #define DBG_PORT_SEL    P0SEL
+       #define DBG_PORT_INP    P0INP
+       #define DBG_PORT_DIR    P0DIR
+
+#endif /* DBG_ON_P0 */
+
+#if COMPANION_CS_ON_P1
+       #define COMPANION_CS_PORT       P1
+       #define COMPANION_CS_SEL        P1SEL
+       #define COMPANION_CS_DIR        P1DIR
+#endif
+
+#if SPI_CS_ON_P1
+       #define SPI_CS_PORT     P1
+       #define SPI_CS_SEL      P1SEL
+       #define SPI_CS_DIR      P1DIR
+#endif
+
+#if SPI_CS_ON_P0
+       #define SPI_CS_PORT     P0
+       #define SPI_CS_SEL      P0SEL
+       #define SPI_CS_DIR      P0DIR
+#endif
+
+#ifndef IGNITE_ON_P2
+#error Please define IGNITE_ON_P2
+#endif
+
+#ifndef IGNITE_ON_P0
+#error Please define IGNITE_ON_P0
+#endif
+
+#ifndef HAS_SERIAL_1
+#error Please define HAS_SERIAL_1
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#ifndef HAS_EEPROM
+#error Please define HAS_EEPROM
+#endif
+
+#ifndef HAS_LOG
+#error Please define HAS_LOG
+#endif
+
+#if HAS_EEPROM
+#ifndef USE_INTERNAL_FLASH
+#error Please define USE_INTERNAL_FLASH
+#endif
+#endif
+
+#ifndef HAS_DBG
+#error Please define HAS_DBG
+#endif
+
+#ifndef HAS_IGNITE
+#error Please define HAS_IGNITE
+#endif
+
+#if HAS_IGNITE
+#define HAS_IGNITE_REPORT 1
+#endif
+
+#ifndef PACKET_HAS_MASTER
+#error Please define PACKET_HAS_MASTER
+#endif
+
+#ifndef PACKET_HAS_SLAVE
+#error Please define PACKET_HAS_SLAVE
+#endif
+
+#ifndef HAS_MONITOR
+#error Please define HAS_MONITOR
+#endif
+
+#if HAS_MONITOR
+#ifndef HAS_RSSI
+#error Please define HAS_RSSI
+#endif
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#if HAS_ADC
+
+#if HAS_ACCEL
+#ifndef HAS_ACCEL_REF
+#error Please define HAS_ACCEL_REF
+#endif
+#else
+#define HAS_ACCEL_REF 0
+#endif
+
+#endif /* HAS_ADC */
+
+#if IGNITE_ON_P2
+#define AO_IGNITER_DROGUE      P2_3
+#define AO_IGNITER_MAIN                P2_4
+#define AO_IGNITER_DIR         P2DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 3)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+#if IGNITE_ON_P0
+#define AO_IGNITER_DROGUE      P0_5
+#define AO_IGNITER_MAIN                P0_4
+#define AO_IGNITER_DIR         P0DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 5)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+/* test these values with real igniters */
+#define AO_IGNITER_OPEN                1000
+#define AO_IGNITER_CLOSED      7000
+#define AO_IGNITER_FIRE_TIME   AO_MS_TO_TICKS(50)
+#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/teleballoon-v1.1/ao_teleballoon.c b/src/teleballoon-v1.1/ao_teleballoon.c
new file mode 100644 (file)
index 0000000..3f12a59
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include "ao_pins.h"
+
+void
+ao_ignite_set_pins(void)
+{
+       AO_IGNITER_DROGUE = 0;
+       AO_IGNITER_MAIN = 0;
+       AO_IGNITER_DIR |= AO_IGNITER_DROGUE_BIT | AO_IGNITER_MAIN_BIT;
+}
+
+void
+main(void)
+{
+       /*
+        * Reduce the transient on the ignite pins at startup by
+        * pulling the pins low as soon as possible at power up
+        */
+       ao_ignite_set_pins();
+
+       ao_clock_init();
+
+       /* Turn on the red LED until the system is stable */
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+
+       /* A hack -- look at the SPI clock pin, if it's sitting at
+        *  ground, then we force the computer to idle mode instead of
+        *  flight mode
+        */
+       if (P1_3 == 0) {
+               ao_flight_force_idle = 1;
+               while (P1_3 == 0)
+                       ;
+       }
+       ao_timer_init();
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_storage_init();
+       ao_flight_init();
+       ao_log_init();
+       ao_report_init();
+       ao_usb_init();
+       ao_serial_init();
+       ao_gps_init();
+       ao_gps_report_init();
+       ao_telemetry_init();
+       ao_radio_init();
+       ao_packet_slave_init(TRUE);
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+#if HAS_COMPANION
+       ao_companion_init();
+#endif
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/telebt-v0.0/.gitignore b/src/telebt-v0.0/.gitignore
new file mode 100644 (file)
index 0000000..1acfbfc
--- /dev/null
@@ -0,0 +1,2 @@
+telebt-*
+ao_product.h
diff --git a/src/telebt-v0.0/.sdcdbrc b/src/telebt-v0.0/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/telebt-v0.0/Makefile b/src/telebt-v0.0/Makefile
new file mode 100644 (file)
index 0000000..e89639a
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# TeleBT v0.0 build
+#
+
+TELEBT_VER=0.0
+TELEBT_DEF=0_0
+
+include ../product/Makefile.telebt
+
diff --git a/src/telebt-v0.1/.gitignore b/src/telebt-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..1acfbfc
--- /dev/null
@@ -0,0 +1,2 @@
+telebt-*
+ao_product.h
diff --git a/src/telebt-v0.1/.sdcdbrc b/src/telebt-v0.1/.sdcdbrc
new file mode 100644 (file)
index 0000000..b9f6129
--- /dev/null
@@ -0,0 +1,2 @@
+--directory=../cc1111:../product:../core:../drivers:.
+
diff --git a/src/telebt-v0.1/Makefile b/src/telebt-v0.1/Makefile
new file mode 100644 (file)
index 0000000..90cd3ca
--- /dev/null
@@ -0,0 +1,21 @@
+#
+# TeleBT v0.1 build
+#
+
+TELEBT_VER=0.1
+TELEBT_DEF=0_1
+
+TELEBT_INC = \
+       ao_25lc1024.h
+
+TELEBT_SRC = \
+       ao_beep.c \
+       ao_log_single.c \
+       ao_log_telem.c \
+       ao_report.c \
+       ao_spi.c \
+       ao_storage.c \
+       ao_m25.c
+
+include ../product/Makefile.telebt
+
diff --git a/src/teledongle-v0.1/.gitignore b/src/teledongle-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..9826814
--- /dev/null
@@ -0,0 +1,2 @@
+teledongle-v0.1*
+ao_product.h
diff --git a/src/teledongle-v0.1/.sdcdbrc b/src/teledongle-v0.1/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/teledongle-v0.1/Makefile b/src/teledongle-v0.1/Makefile
new file mode 100644 (file)
index 0000000..4842510
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# TeleDongle v0.2 build
+#
+
+TD_VER=0.1
+TD_DEF=0_1
+
+include ../product/Makefile.teledongle
diff --git a/src/teledongle-v0.2/.gitignore b/src/teledongle-v0.2/.gitignore
new file mode 100644 (file)
index 0000000..f6ea8c6
--- /dev/null
@@ -0,0 +1,2 @@
+teledongle-v0.2*
+ao_product.h
diff --git a/src/teledongle-v0.2/.sdcdbrc b/src/teledongle-v0.2/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/teledongle-v0.2/Makefile b/src/teledongle-v0.2/Makefile
new file mode 100644 (file)
index 0000000..ce4ab43
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# TeleDongle v0.2 build
+#
+
+TD_VER=0.2
+TD_DEF=0_2
+
+include ../product/Makefile.teledongle
\ No newline at end of file
diff --git a/src/telefire-v0.1/.gitignore b/src/telefire-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..4d4f420
--- /dev/null
@@ -0,0 +1,2 @@
+telefire-*
+ao_product.h
diff --git a/src/telefire-v0.1/.sdcdbrc b/src/telefire-v0.1/.sdcdbrc
new file mode 100644 (file)
index 0000000..b9f6129
--- /dev/null
@@ -0,0 +1,2 @@
+--directory=../cc1111:../product:../core:../drivers:.
+
diff --git a/src/telefire-v0.1/Makefile b/src/telefire-v0.1/Makefile
new file mode 100644 (file)
index 0000000..712b2e8
--- /dev/null
@@ -0,0 +1,103 @@
+#
+# TeleFire build file
+#
+
+TELEFIRE_VER=0.1
+TELEFIRE_DEF=0_1
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_pad.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_convert.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_adc.c \
+       ao_aes.c \
+       ao_beep.c \
+       ao_dma.c \
+       ao_intflash.c \
+       ao_radio.c \
+       ao_radio_cmac.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_spi.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC = \
+       ao_pca9922.c \
+       ao_74hc497.c \
+       ao_pad.c \
+       ao_radio_cmac_cmd.c
+
+PRODUCT_SRC = \
+       ao_telefire.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = telefire-v$(TELEFIRE_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleFire-v$(TELEFIRE_VER)
+PRODUCT_DEF=-DTELEFIRE_V_$(TELEFIRE_DEF)
+IDPRODUCT=0x000f
+CODESIZE=0x6700
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM)  || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
+
diff --git a/src/telefire-v0.1/ao_pins.h b/src/telefire-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..eecf783
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#define HAS_RADIO      1
+
+#define HAS_FLIGHT             0
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define HAS_GPS                        0
+#define HAS_SERIAL_1           0
+#define HAS_ADC                        1
+#define HAS_DBG                        0
+#define HAS_EEPROM             1
+#define HAS_LOG                        0
+#define HAS_PAD                        1
+#define USE_INTERNAL_FLASH     1
+#define DBG_ON_P1              0
+#define IGNITE_ON_P2           0
+#define IGNITE_ON_P1           1
+#define IGNITE_ON_P0           0
+#define PACKET_HAS_MASTER      0
+#define PACKET_HAS_SLAVE       0
+
+#define AO_LED_CONTINUITY(c)   (1 << (c))
+#define AO_LED_CONTINUITY_MASK (0xf)
+#define AO_LED_RX              0x10
+#define AO_LED_TX              0x20
+#define AO_LED_ARMED           0x40
+#define AO_LED_POWER           0x80
+
+#define AO_LED_RED             AO_LED_TX
+#define AO_LED_GREEN           AO_LED_RX
+
+#define LEDS_AVAILABLE         (0xff)
+#define HAS_EXTERNAL_TEMP      0
+#define HAS_ACCEL_REF          0
+#define SPI_CS_ON_P1           1
+#define HAS_AES                        1
+#define DMA_SHARE_AES_RADIO    1
+
+#define SPI_CS_PORT    P1
+#define SPI_CS_SEL     P1SEL
+#define SPI_CS_DIR     P1DIR
+
+#define SPI_CONST      0x00
+
+#define HAS_SPI_0              0
+#define HAS_SPI_1              1
+#define SPI_1_ALT_1            0
+#define SPI_1_ALT_2            1
+
+#define AO_74HC497_CS_PORT     P1
+#define AO_74HC497_CS_PIN      4
+#define AO_74HC497_CS          P1_4
+
+#define AO_PCA9922_CS_PORT     P1
+#define AO_PCA9922_CS_PIN      4
+#define AO_PCA9922_CS          P1_4
+
+#define AO_PAD_NUM             4
+#define        AO_PAD_PORT             P1
+#define AO_PAD_DIR             P1DIR
+#define AO_PAD_PIN_0           0
+#define AO_PAD_0               P1_0
+#define AO_PAD_PIN_1           1
+#define AO_PAD_1               P1_1
+#define AO_PAD_PIN_2           2
+#define AO_PAD_2               P1_2
+#define AO_PAD_PIN_3           3
+#define AO_PAD_3               P1_3
+#define AO_PAD_ALL_PINS                ((1 << AO_PAD_PIN_0) | (1 << AO_PAD_PIN_1) | (1 << AO_PAD_PIN_2) | (1 << AO_PAD_PIN_3))
+
+/* test these values with real igniters */
+#define AO_PAD_RELAY_CLOSED    3524
+#define AO_PAD_NO_IGNITER      16904
+#define AO_PAD_GOOD_IGNITER    22514
+
+struct ao_adc {
+       int16_t         sense[4];
+       int16_t         pyro;
+       int16_t         batt;
+};
+
+#define AO_ADC_DUMP(p)                                                 \
+       printf ("tick: %5u 0: %5d 1: %5d 2: %5d 3: %5d pyro: %5d batt %5d\n", \
+               (p)->tick,                                              \
+               (p)->adc.sense[0],                                      \
+               (p)->adc.sense[1],                                      \
+               (p)->adc.sense[2],                                      \
+               (p)->adc.sense[3],                                      \
+               (p)->adc.pyro,                                          \
+               (p)->adc.batt)
+
+#define AO_ADC_PINS    ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4) | (1 << 5))
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telefire-v0.1/ao_telefire.c b/src/telefire-v0.1/ao_telefire.c
new file mode 100644 (file)
index 0000000..cc0f668
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_pad.h>
+#include <ao_74hc497.h>
+#include <ao_radio_cmac_cmd.h>
+
+void
+main(void)
+{
+       ao_clock_init();
+
+       ao_led_init(LEDS_AVAILABLE);
+
+       ao_timer_init();
+       ao_adc_init();
+       ao_beep_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_74hc497_init();
+       ao_storage_init();
+       ao_usb_init();
+       ao_radio_init();
+       ao_aes_init();
+       ao_pad_init();
+//     ao_radio_cmac_cmd_init();
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/telelaunch-v0.1/.gitignore b/src/telelaunch-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..e2cf954
--- /dev/null
@@ -0,0 +1,2 @@
+telelaunch*
+ao_product.h
diff --git a/src/telelaunch-v0.1/.sdcdbrc b/src/telelaunch-v0.1/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/telelaunch-v0.1/Makefile b/src/telelaunch-v0.1/Makefile
new file mode 100644 (file)
index 0000000..b0d9d16
--- /dev/null
@@ -0,0 +1,4 @@
+TELELAUNCH_VER=0.1
+TELELAUNCH_DEF=0_1
+
+include ../product/Makefile.telelaunch
diff --git a/src/telelaunch-v0.1/Makefile.defs b/src/telelaunch-v0.1/Makefile.defs
new file mode 100644 (file)
index 0000000..56f5730
--- /dev/null
@@ -0,0 +1,14 @@
+PROG = telelaunch-v0.1-$(VERSION).ihx
+
+SRC = \
+       $(TLAUNCH_BASE_SRC) \
+       $(SPI_DRIVER_SRC) \
+       $(EE_DRIVER_SRC) \
+       ao_launch.c \
+       ao_aes.c \
+       ao_radio_cmac.c \
+       $(DBG_SRC)
+
+PRODUCT=TeleLaunch-v0.1
+PRODUCT_DEF=-DTELELAUNCH_V_0_1
+IDPRODUCT=0x000f
diff --git a/src/telelco-v0.1/.gitignore b/src/telelco-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..54267ec
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+telelco-*
diff --git a/src/telelco-v0.1/Makefile b/src/telelco-v0.1/Makefile
new file mode 100644 (file)
index 0000000..d2702dd
--- /dev/null
@@ -0,0 +1,96 @@
+#
+# AltOS build for TeleLCO
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_companion.h \
+       ao_data.h \
+       ao_sample.h \
+       ao_pins.h \
+       ao_product.h \
+       ao_seven_segment.h \
+       ao_lco.h \
+       ao_lco_cmd.h \
+       ao_lco_func.h \
+       ao_radio_spi.h \
+       ao_radio_cmac.h \
+       stm32l.h
+
+#
+# Common AltOS sources
+#
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+ALTOS_SRC = \
+       ao_interrupt.c \
+       ao_product.c \
+       ao_romconfig.c \
+       ao_cmd.c \
+       ao_config.c \
+       ao_task.c \
+       ao_led.c \
+       ao_stdio.c \
+       ao_panic.c \
+       ao_timer.c \
+       ao_mutex.c \
+       ao_freq.c \
+       ao_dma_stm.c \
+       ao_spi_stm.c \
+       ao_beep_stm.c \
+       ao_storage.c \
+       ao_eeprom_stm.c \
+       ao_lcd_stm.c \
+       ao_usb_stm.c \
+       ao_exti_stm.c \
+       ao_radio_master.c \
+       ao_seven_segment.c \
+       ao_quadrature.c \
+       ao_button.c \
+       ao_event.c \
+       ao_lco.c \
+       ao_lco_cmd.c \
+       ao_lco_func.c \
+       ao_radio_cmac_cmd.c
+
+PRODUCT=TeleLCO-v0.1
+PRODUCT_DEF=-DMEGAMETRUM
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g
+
+PROGNAME=telelco-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+
+SRC=$(ALTOS_SRC) ao_telelco.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG)
+
+$(PROG): Makefile $(OBJ) altos.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(SAT_CLIB) -lgcc
+
+../altitude.h: make-altitude
+       nickle $< > $@
+
+$(OBJ): $(INC)
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telelco-v0.1/ao_lco.c b/src/telelco-v0.1/ao_lco.c
new file mode 100644 (file)
index 0000000..41bba0b
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_lco.h>
+#include <ao_event.h>
+#include <ao_seven_segment.h>
+#include <ao_quadrature.h>
+#include <ao_lco_func.h>
+#include <ao_radio_cmac.h>
+
+#define DEBUG  1
+
+#if DEBUG
+static uint8_t ao_lco_debug;
+#define PRINTD(...) do { if (!ao_lco_debug) break; printf ("\r%5u %s: ", ao_tick_count, __func__); printf(__VA_ARGS__); flush(); } while(0)
+#else
+#define PRINTD(...) 
+#endif
+
+#define AO_LCO_PAD_DIGIT       0
+#define AO_LCO_BOX_DIGIT_1     1
+#define AO_LCO_BOX_DIGIT_10    2
+
+static uint8_t ao_lco_min_box, ao_lco_max_box;
+static uint8_t ao_lco_mutex;
+static uint8_t ao_lco_pad;
+static uint8_t ao_lco_box;
+static uint8_t ao_lco_armed;
+static uint8_t ao_lco_firing;
+static uint8_t ao_lco_valid;
+
+static void
+ao_lco_set_pad(void)
+{
+       ao_seven_segment_set(AO_LCO_PAD_DIGIT, ao_lco_pad + 1);
+}
+
+static void
+ao_lco_set_box(void)
+{
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, ao_lco_box % 10);
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, ao_lco_box / 10);
+}
+
+#define MASK_SIZE(n)   (((n) + 7) >> 3)
+#define MASK_ID(n)     ((n) >> 3)
+#define MASK_SHIFT(n)  ((n) & 7)
+
+static uint8_t ao_lco_box_mask[MASK_SIZE(AO_PAD_MAX_BOXES)];
+
+static uint8_t
+ao_lco_box_present(uint8_t box)
+{
+       if (box >= AO_PAD_MAX_BOXES)
+               return 0;
+       return (ao_lco_box_mask[MASK_ID(box)] >> MASK_SHIFT(box)) & 1;
+}
+
+static void
+ao_lco_input(void)
+{
+       static struct ao_event  event;
+       int8_t  dir;
+
+       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+       ao_lco_set_pad();
+       ao_lco_set_box();
+       for (;;) {
+               ao_event_get(&event);
+               PRINTD("event type %d unit %d value %d\n",
+                      event.type, event.unit, event.value);
+               switch (event.type) {
+               case AO_EVENT_QUADRATURE:
+                       switch (event.unit) {
+                       case AO_QUADRATURE_PAD:
+                               if (!ao_lco_armed) {
+                                       ao_lco_pad = event.value & 3;
+                                       ao_quadrature_count[AO_QUADRATURE_PAD] = ao_lco_pad;
+                                       ao_lco_set_pad();
+                               }
+                               break;
+                       case AO_QUADRATURE_BOX:
+                               if (!ao_lco_armed) {
+                                       if (event.value == ao_lco_box)
+                                               break;
+                                       dir = (event.value - ao_lco_box) > 0 ? 1 : -1;
+                                       ao_lco_box = event.value;
+                                       while (!ao_lco_box_present(ao_lco_box)) {
+                                               ao_lco_box += dir;
+                                               if (ao_lco_box > ao_lco_max_box)
+                                                       ao_lco_box = ao_lco_min_box;
+                                               else if (ao_lco_box < ao_lco_min_box)
+                                                       ao_lco_box = ao_lco_min_box;
+                                       }
+                                       ao_quadrature_count[AO_QUADRATURE_PAD] = ao_lco_box;
+                                       ao_lco_set_box();
+                               }
+                               break;
+                       }
+                       break;
+               case AO_EVENT_BUTTON:
+                       switch (event.unit) {
+                       case AO_BUTTON_ARM:
+                               ao_lco_armed = event.value;
+                               PRINTD("Armed %d\n", ao_lco_armed);
+                               ao_wakeup(&ao_lco_armed);
+                               break;
+                       case AO_BUTTON_FIRE:
+                               if (ao_lco_armed) {
+                                       ao_lco_firing = event.value;
+                                       PRINTD("Firing %d\n", ao_lco_firing);
+                                       ao_wakeup(&ao_lco_armed);
+                               }
+                               break;
+                       }
+                       break;
+               }
+       }
+}
+
+static AO_LED_TYPE     continuity_led[AO_LED_CONTINUITY_NUM] = {
+#ifdef AO_LED_CONTINUITY_0
+       AO_LED_CONTINUITY_0,
+#endif
+#ifdef AO_LED_CONTINUITY_1
+       AO_LED_CONTINUITY_1,
+#endif
+#ifdef AO_LED_CONTINUITY_2
+       AO_LED_CONTINUITY_2,
+#endif
+#ifdef AO_LED_CONTINUITY_3
+       AO_LED_CONTINUITY_3,
+#endif
+#ifdef AO_LED_CONTINUITY_4
+       AO_LED_CONTINUITY_4,
+#endif
+#ifdef AO_LED_CONTINUITY_5
+       AO_LED_CONTINUITY_5,
+#endif
+#ifdef AO_LED_CONTINUITY_6
+       AO_LED_CONTINUITY_6,
+#endif
+#ifdef AO_LED_CONTINUITY_7
+       AO_LED_CONTINUITY_7,
+#endif
+};
+
+static uint16_t        ao_lco_tick_offset;
+
+static struct ao_pad_query     ao_pad_query;
+
+static void
+ao_lco_update(void)
+{
+       int8_t                  r;
+       uint8_t                 c;
+
+       r = ao_lco_query(ao_lco_box, &ao_pad_query, &ao_lco_tick_offset);
+       if (r == AO_RADIO_CMAC_OK)
+               ao_lco_valid = 1;
+
+#if 0
+       PRINTD("lco_query success arm_status %d i0 %d i1 %d i2 %d i3 %d\n",
+              query.arm_status,
+              query.igniter_status[0],
+              query.igniter_status[1],
+              query.igniter_status[2],
+              query.igniter_status[3]);
+#endif
+
+       ao_wakeup(&ao_pad_query);
+}
+
+static void
+ao_lco_box_set_present(uint8_t box)
+{
+       if (box >= AO_PAD_MAX_BOXES)
+               return;
+       ao_lco_box_mask[MASK_ID(box)] |= 1 << MASK_SHIFT(box);
+}
+
+static void
+ao_lco_search(void)
+{
+       uint16_t        tick_offset;
+       int8_t          r;
+
+       ao_lco_min_box = 0xff;
+       ao_lco_max_box = 0x00;
+       for (ao_lco_box = 0; ao_lco_box < AO_PAD_MAX_BOXES; ao_lco_box++) {
+               ao_lco_set_box();
+               r = ao_lco_query(ao_lco_box, &ao_pad_query, &ao_lco_tick_offset);
+               if (r == AO_RADIO_CMAC_OK) {
+                       if (ao_lco_box < ao_lco_min_box)
+                               ao_lco_min_box = ao_lco_box;
+                       if (ao_lco_box > ao_lco_max_box)
+                               ao_lco_max_box = ao_lco_box;
+                       ao_lco_box_set_present(ao_lco_box);
+               }
+       }
+       ao_lco_box = ao_lco_min_box;
+       ao_lco_pad = 0;
+}
+
+static void
+ao_lco_igniter_status(void)
+{
+       uint8_t         c;
+       uint16_t        delay;
+
+       for (;;) {
+//             ao_alarm(delay);
+               ao_sleep(&ao_pad_query);
+//             ao_clear_alarm();
+               if (!ao_lco_valid) {
+                       ao_led_on(AO_LED_RED);
+                       ao_led_off(AO_LED_GREEN);
+                       continue;
+               }
+               PRINTD("RSSI %d\n", ao_radio_cmac_rssi);
+               if (ao_radio_cmac_rssi < -70)
+                       ao_led_on(AO_LED_RED|AO_LED_GREEN);
+               else {
+                       ao_led_on(AO_LED_GREEN);
+                       ao_led_off(AO_LED_RED);
+               }
+               if (ao_pad_query.arm_status)
+                       ao_led_on(AO_LED_REMOTE_ARM);
+               else
+                       ao_led_off(AO_LED_REMOTE_ARM);
+               for (c = 0; c < AO_LED_CONTINUITY_NUM; c++) {
+                       uint8_t status;
+
+                       if (ao_pad_query.channels & (1 << c))
+                               status = ao_pad_query.igniter_status[c];
+                       else
+                               status = AO_PAD_IGNITER_STATUS_NO_IGNITER_RELAY_OPEN;
+                       if (status == AO_PAD_IGNITER_STATUS_GOOD_IGNITER_RELAY_OPEN)
+                               ao_led_on(continuity_led[c]);
+                       else
+                               ao_led_off(continuity_led[c]);
+               }
+       }
+}
+
+static void
+ao_lco_arm_warn(void)
+{
+       for (;;) {
+               while (!ao_lco_armed)
+                       ao_sleep(&ao_lco_armed);
+               ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+               ao_delay(AO_MS_TO_TICKS(200));
+       }
+}
+
+static struct ao_task ao_lco_input_task;
+static struct ao_task ao_lco_monitor_task;
+static struct ao_task ao_lco_arm_warn_task;
+static struct ao_task ao_lco_igniter_status_task;
+
+static void
+ao_lco_monitor(void)
+{
+       uint16_t                delay;
+
+       ao_lco_search();
+       ao_add_task(&ao_lco_input_task, ao_lco_input, "lco input");
+       ao_add_task(&ao_lco_arm_warn_task, ao_lco_arm_warn, "lco arm warn");
+       ao_add_task(&ao_lco_igniter_status_task, ao_lco_igniter_status, "lco igniter status");
+       for (;;) {
+               PRINTD("monitor armed %d firing %d offset %d\n",
+                      ao_lco_armed, ao_lco_firing, ao_lco_tick_offset);
+
+               if (ao_lco_armed && ao_lco_firing) {
+                       PRINTD("Firing box %d pad %d: valid %d\n",
+                              ao_lco_box, ao_lco_pad, ao_lco_valid);
+                       if (!ao_lco_valid)
+                               ao_lco_update();
+                       if (ao_lco_valid)
+                               ao_lco_ignite(ao_lco_box, 1 << ao_lco_pad, ao_lco_tick_offset);
+               } else if (ao_lco_armed) {
+                       PRINTD("Arming box %d pad %d\n",
+                              ao_lco_box, ao_lco_pad);
+                       if (!ao_lco_valid)
+                               ao_lco_update();
+                       ao_lco_arm(ao_lco_box, 1 << ao_lco_pad, ao_lco_tick_offset);
+                       ao_lco_update();
+               } else {
+                       ao_lco_update();
+               }
+               if (ao_lco_armed && ao_lco_firing)
+                       delay = AO_MS_TO_TICKS(100);
+               else
+                       delay = AO_SEC_TO_TICKS(1);
+               ao_alarm(delay);
+               ao_sleep(&ao_lco_armed);
+               ao_clear_alarm();
+       }
+}
+
+#if DEBUG
+void
+ao_lco_set_debug(void)
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status == ao_cmd_success)
+               ao_lco_debug = ao_cmd_lex_i != 0;
+}
+
+__code struct ao_cmds ao_lco_cmds[] = {
+       { ao_lco_set_debug,     "D <0 off, 1 on>\0Debug" },
+       { 0, NULL }
+};
+#endif
+
+void
+ao_lco_init(void)
+{
+       ao_add_task(&ao_lco_monitor_task, ao_lco_monitor, "lco monitor");
+#if DEBUG
+       ao_cmd_register(&ao_lco_cmds[0]);
+#endif
+}
diff --git a/src/telelco-v0.1/ao_lco.h b/src/telelco-v0.1/ao_lco.h
new file mode 100644 (file)
index 0000000..253f970
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_LCO_H_
+#define _AO_LCO_H_
+
+void
+ao_lco_init(void);
+
+#endif /* _AO_LCO_H_ */
diff --git a/src/telelco-v0.1/ao_pins.h b/src/telelco-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..60cf018
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* 8MHz High speed external crystal */
+#define AO_HSE                 8000000
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL              12
+#define AO_RCC_CFGR_PLLMUL     (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 32MHz (no need to go faster than CPU) */
+#define AO_PLLDIV              3
+#define AO_RCC_CFGR_PLLDIV     (STM_RCC_CFGR_PLLDIV_3)
+
+/* HCLK = 32MHz (CPU clock) */
+#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at 16MHz (HCLK/2) */
+#define AO_APB1_PRESCALER      2
+#define AO_RCC_CFGR_PPRE1_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+/* Run APB2 at 16MHz (HCLK/2) */
+#define AO_APB2_PRESCALER      2
+#define AO_RCC_CFGR_PPRE2_DIV  STM_RCC_CFGR_PPRE2_DIV_2
+
+#define HAS_EEPROM             1
+#define USE_INTERNAL_FLASH     1
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define HAS_RADIO              1
+#define HAS_TELEMETRY          0
+#define HAS_AES                        1
+
+#define HAS_SPI_1              1
+#define SPI_1_PA5_PA6_PA7      1
+#define SPI_1_PB3_PB4_PB5      0
+#define SPI_1_PE13_PE14_PE15   0
+
+#define HAS_SPI_2              1
+#define SPI_2_PB13_PB14_PB15   1
+#define SPI_2_PD1_PD3_PD4      0
+#define SPI_2_GPIO             (&stm_gpiob)
+#define SPI_2_SCK              13
+#define SPI_2_MISO             14
+#define SPI_2_MOSI             15
+
+#define HAS_I2C_1              0
+
+#define HAS_I2C_2              0
+
+#define PACKET_HAS_SLAVE       0
+#define PACKET_HAS_MASTER      0
+
+/*
+ * Radio is a cc1111 connected via SPI
+ */
+#define AO_RADIO_CAL_DEFAULT   1186611
+
+#define AO_RADIO_SPI_BUS       AO_SPI_2_PB13_PB14_PB15
+#define AO_RADIO_CS_PORT       (&stm_gpiob)
+#define AO_RADIO_CS_PIN                12
+
+#define AO_RADIO_INT_PORT      (&stm_gpioc)
+#define AO_RADIO_INT_PIN       14
+
+#define LOW_LEVEL_DEBUG                0
+
+#define LED_PORT_ENABLE                STM_RCC_AHBENR_GPIOCEN
+#define LED_PORT               (&stm_gpioc)
+#define LED_PIN_RED            7
+#define LED_PIN_GREEN          8
+#define LED_PIN_CONTINUITY_3   9
+#define LED_PIN_CONTINUITY_2   10
+#define LED_PIN_CONTINUITY_1   11
+#define LED_PIN_CONTINUITY_0   12
+#define LED_PIN_REMOTE_ARM     13
+#define AO_LED_RED             (1 << LED_PIN_RED)
+#define AO_LED_GREEN           (1 << LED_PIN_GREEN)
+#define AO_LED_CONTINUITY_3    (1 << LED_PIN_CONTINUITY_3)
+#define AO_LED_CONTINUITY_2    (1 << LED_PIN_CONTINUITY_2)
+#define AO_LED_CONTINUITY_1    (1 << LED_PIN_CONTINUITY_1)
+#define AO_LED_CONTINUITY_0    (1 << LED_PIN_CONTINUITY_0)
+
+#define AO_LED_CONTINUITY_NUM  4
+
+#define AO_LED_REMOTE_ARM      (1 << LED_PIN_REMOTE_ARM)
+
+#define LEDS_AVAILABLE         (AO_LED_RED |           \
+                                AO_LED_GREEN |         \
+                                AO_LED_CONTINUITY_3 |  \
+                                AO_LED_CONTINUITY_2 |  \
+                                AO_LED_CONTINUITY_1 |  \
+                                AO_LED_CONTINUITY_0 |  \
+                                AO_LED_REMOTE_ARM)
+
+/* LCD displays */
+
+#define AO_LCD_STM_SEG_ENABLED_0 (             \
+               (1 << 0) | /* PA1 */            \
+               (1 << 1) | /* PA2 */            \
+               (1 << 2) | /* PA3 */            \
+               (1 << 3) | /* PA6 */            \
+               (1 << 4) | /* PA7 */            \
+               (1 << 5) | /* PB0 */            \
+               (1 << 6) | /* PB1 */            \
+               (1 << 7) | /* PB3 */            \
+               (0 << 8) | /* PB4 */            \
+               (0 << 9) | /* PB5 */            \
+               (0 << 10) | /* PB10 */          \
+               (0 << 11) | /* PB11 */          \
+               (0 << 12) | /* PB12 */          \
+               (0 << 13) | /* PB13 */          \
+               (0 << 14) | /* PB14 */          \
+               (0 << 15) | /* PB15 */          \
+               (0 << 16) | /* PB8 */           \
+               (0 << 17) | /* PA15 */          \
+               (0 << 18) | /* PC0 */           \
+               (0 << 19) | /* PC1 */           \
+               (0 << 20) | /* PC2 */           \
+               (0 << 21) | /* PC3 */           \
+               (0 << 22) | /* PC4 */           \
+               (0 << 23) | /* PC5 */           \
+               (0 << 24) | /* PC6 */           \
+               (0 << 25) | /* PC7 */           \
+               (0 << 26) | /* PC8 */           \
+               (0 << 27) | /* PC9 */           \
+               (0 << 28) | /* PC10 or PD8 */   \
+               (0 << 29) | /* PC11 or PD9 */   \
+               (0 << 30) | /* PC12 or PD10 */  \
+               (0 << 31))  /* PD2 or PD11 */
+
+#define AO_LCD_STM_SEG_ENABLED_1 (             \
+               (0 << 0) | /* PD12 */           \
+               (0 << 1) | /* PD13 */           \
+               (0 << 2) | /* PD14 */           \
+               (0 << 3) | /* PD15 */           \
+               (0 << 4) | /* PE0 */            \
+               (0 << 5) | /* PE1 */            \
+               (0 << 6) | /* PE2 */            \
+               (0 << 7))  /* PE3 */
+
+#define AO_LCD_STM_COM_ENABLED (               \
+               (1 << 0) | /* PA8 */            \
+               (1 << 1) | /* PA9 */            \
+               (1 << 2) | /* PA10 */           \
+               (0 << 3) | /* PB9 */            \
+               (0 << 4) | /* PC10 */           \
+               (0 << 5) | /* PC11 */           \
+               (0 << 6)) /* PC12 */
+
+#define AO_LCD_28_ON_C 0
+
+#define AO_LCD_DUTY    STM_LCD_CR_DUTY_1_4
+
+#define AO_SEGMENT_0           0
+#define AO_SEGMENT_1           5
+#define AO_SEGMENT_2           1
+#define AO_SEGMENT_3           6
+#define AO_SEGMENT_4           4
+#define AO_SEGMENT_5           2
+#define AO_SEGMENT_6           3
+#define AO_SEGMENT_7           7
+
+/*
+ * Use event queue for input devices
+ */
+
+#define AO_EVENT               1
+
+/*
+ * Knobs
+ */
+
+#define AO_QUADRATURE_COUNT    2
+#define AO_QUADRATURE_MODE     0
+
+#define AO_QUADRATURE_0_PORT   &stm_gpioc
+#define AO_QUADRATURE_0_A      3
+#define AO_QUADRATURE_0_B      2
+
+#define AO_QUADRATURE_PAD      0
+
+#define AO_QUADRATURE_1_PORT   &stm_gpioc
+#define AO_QUADRATURE_1_A      1
+#define AO_QUADRATURE_1_B      0
+
+#define AO_QUADRATURE_BOX      1
+
+/*
+ * Buttons
+ */
+
+#define AO_BUTTON_COUNT                2
+#define AO_BUTTON_MODE         AO_EXTI_MODE_PULL_UP
+
+#define AO_BUTTON_0_PORT       &stm_gpioc
+#define AO_BUTTON_0            4
+
+#define AO_BUTTON_ARM          0
+
+#define AO_BUTTON_1_PORT       &stm_gpioc
+#define AO_BUTTON_1            5
+
+#define AO_BUTTON_FIRE         1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telelco-v0.1/ao_telelco.c b/src/telelco-v0.1/ao_telelco.c
new file mode 100644 (file)
index 0000000..080a140
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao.h>
+#include <ao_exti.h>
+#include <ao_packet.h>
+#include <ao_companion.h>
+#include <ao_profile.h>
+#include <ao_pyro.h>
+#include <ao_aes.h>
+#include <ao_seven_segment.h>
+#include <ao_quadrature.h>
+#include <ao_button.h>
+#include <ao_lco.h>
+#include <ao_lco_cmd.h>
+#include <ao_radio_cmac_cmd.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+       
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_GREEN);
+       ao_timer_init();
+
+       ao_spi_init();
+       ao_dma_init();
+       ao_exti_init();
+
+       ao_beep_init();
+       ao_cmd_init();
+
+       ao_lcd_stm_init();
+       ao_seven_segment_init();
+       ao_quadrature_init();
+       ao_button_init();
+
+       ao_storage_init();
+       
+       ao_radio_init();
+
+       ao_usb_init();
+
+       ao_config_init();
+       
+       ao_quadrature_init();
+       ao_button_init();
+       ao_lco_init();
+       ao_lco_cmd_init();
+//     ao_radio_cmac_cmd_init();
+       
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telemetrum-v0.1-sirf/.gitignore b/src/telemetrum-v0.1-sirf/.gitignore
new file mode 100644 (file)
index 0000000..7698f5a
--- /dev/null
@@ -0,0 +1,2 @@
+telemetrum-v0.1-sirf*
+ao_product.h
diff --git a/src/telemetrum-v0.1-sirf/Makefile b/src/telemetrum-v0.1-sirf/Makefile
new file mode 100644 (file)
index 0000000..d138b5e
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# TeleMetrum v0.1 with SkyTraq GPS build
+#
+
+TM_VER=0.1
+TM_DEF=0_1
+TM_EXTRA=-sirf
+
+TM_INC = \
+       ao_25lc1024.h
+
+TM_SRC = \
+       ao_gps_sirf.c \
+       ao_25lc1024.c
+
+include ../product/Makefile.telemetrum
+
diff --git a/src/telemetrum-v0.1-sky/.gitignore b/src/telemetrum-v0.1-sky/.gitignore
new file mode 100644 (file)
index 0000000..d25d7ad
--- /dev/null
@@ -0,0 +1,2 @@
+telemetrum-v0.1-sky*
+ao_product.h
diff --git a/src/telemetrum-v0.1-sky/.sdcdbrc b/src/telemetrum-v0.1-sky/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/telemetrum-v0.1-sky/Makefile b/src/telemetrum-v0.1-sky/Makefile
new file mode 100644 (file)
index 0000000..69cd346
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# TeleMetrum v0.1 with SkyTraq GPS build
+#
+
+TM_VER=0.1
+TM_DEF=0_1
+TM_EXTRA=-sky
+
+TM_INC = \
+       ao_25lc1024.h
+
+TM_SRC = \
+       ao_gps_skytraq.c \
+       ao_25lc1024.c
+
+include ../product/Makefile.telemetrum
+
diff --git a/src/telemetrum-v1.0/.gitignore b/src/telemetrum-v1.0/.gitignore
new file mode 100644 (file)
index 0000000..c221215
--- /dev/null
@@ -0,0 +1,2 @@
+telemetrum-*
+ao_product.h
diff --git a/src/telemetrum-v1.0/.sdcdbrc b/src/telemetrum-v1.0/.sdcdbrc
new file mode 100644 (file)
index 0000000..fbe9a59
--- /dev/null
@@ -0,0 +1 @@
+--directory=../cc1111:../product:../core:../drivers:.
diff --git a/src/telemetrum-v1.0/Makefile b/src/telemetrum-v1.0/Makefile
new file mode 100644 (file)
index 0000000..4aae84c
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# TeleMetrum v1.0 build
+#
+
+TM_VER=1.0
+TM_DEF=1_0
+
+TM_INC = \
+       ao_at45db161d.h
+
+TM_SRC = \
+       ao_companion.c \
+       ao_gps_skytraq.c \
+       ao_at45db161d.c
+
+include ../product/Makefile.telemetrum
diff --git a/src/telemetrum-v1.1/.gitignore b/src/telemetrum-v1.1/.gitignore
new file mode 100644 (file)
index 0000000..c221215
--- /dev/null
@@ -0,0 +1,2 @@
+telemetrum-*
+ao_product.h
diff --git a/src/telemetrum-v1.1/.sdcdbrc b/src/telemetrum-v1.1/.sdcdbrc
new file mode 100644 (file)
index 0000000..fbe9a59
--- /dev/null
@@ -0,0 +1 @@
+--directory=../cc1111:../product:../core:../drivers:.
diff --git a/src/telemetrum-v1.1/Makefile b/src/telemetrum-v1.1/Makefile
new file mode 100644 (file)
index 0000000..4bea03d
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# AltOS build
+#
+#
+
+TM_VER=1.1
+TM_DEF=1_1
+
+TM_INC =
+
+TM_SRC = \
+       ao_companion.c \
+       ao_gps_skytraq.c \
+       ao_m25.c
+
+include ../product/Makefile.telemetrum
diff --git a/src/telemetrum-v1.2/.gitignore b/src/telemetrum-v1.2/.gitignore
new file mode 100644 (file)
index 0000000..c221215
--- /dev/null
@@ -0,0 +1,2 @@
+telemetrum-*
+ao_product.h
diff --git a/src/telemetrum-v1.2/.sdcdbrc b/src/telemetrum-v1.2/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/telemetrum-v1.2/Makefile b/src/telemetrum-v1.2/Makefile
new file mode 100644 (file)
index 0000000..4b650ad
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# AltOS build
+#
+#
+
+TM_VER=1.2
+TM_DEF=1_2
+
+TM_INC =
+
+TM_SRC = \
+       ao_companion.c \
+       ao_gps_skytraq.c \
+       ao_m25.c
+
+include ../product/Makefile.telemetrum
diff --git a/src/telemini-v1.0/.gitignore b/src/telemini-v1.0/.gitignore
new file mode 100644 (file)
index 0000000..82868aa
--- /dev/null
@@ -0,0 +1,2 @@
+telemini-*
+ao_product.h
diff --git a/src/telemini-v1.0/.sdcdbrc b/src/telemini-v1.0/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/telemini-v1.0/Makefile b/src/telemini-v1.0/Makefile
new file mode 100644 (file)
index 0000000..4f1c8b5
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# TeleMini build file
+#
+
+TELEMINI_VER=1.0
+TELEMINI_DEF=1_0
+
+include ../product/Makefile.telemini
diff --git a/src/telenano-v0.1/.gitignore b/src/telenano-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..0412f7d
--- /dev/null
@@ -0,0 +1,2 @@
+telenano-*
+ao_product.h
diff --git a/src/telenano-v0.1/.sdcdbrc b/src/telenano-v0.1/.sdcdbrc
new file mode 100644 (file)
index 0000000..710b4a2
--- /dev/null
@@ -0,0 +1 @@
+--directory=..
diff --git a/src/telenano-v0.1/Makefile b/src/telenano-v0.1/Makefile
new file mode 100644 (file)
index 0000000..2714c1e
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# TeleNano build file
+#
+
+TELENANO_VER=0.1
+TELENANO_DEF=0_1
+
+include ../product/Makefile.telenano
+
diff --git a/src/telepyro-v0.1/.gitignore b/src/telepyro-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..5166481
--- /dev/null
@@ -0,0 +1,2 @@
+telepyro-v0.1*
+ao_product.h
diff --git a/src/telepyro-v0.1/Makefile b/src/telepyro-v0.1/Makefile
new file mode 100644 (file)
index 0000000..2ccd565
--- /dev/null
@@ -0,0 +1,106 @@
+#
+# AltOS build
+#
+#
+vpath % .:..:../core:../product:../drivers:../avr
+vpath ao-make-product.5c ../util
+
+MCU=atmega32u4
+DUDECPUTYPE=m32u4
+#PROGRAMMER=stk500v2 -P usb
+PROGRAMMER=usbtiny
+LOADCMD=avrdude
+LOADARG=-p $(DUDECPUTYPE) -c $(PROGRAMMER) -e -U flash:w:
+CC=avr-gcc
+OBJCOPY=avr-objcopy
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_usb.h \
+       ao_pins.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_product.h
+
+ALTOS_SRC = \
+       ao_clock.c \
+       ao_cmd.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_product.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_timer.c \
+       ao_led.c \
+       ao_avr_stdio.c \
+       ao_romconfig.c \
+       ao_usb_avr.c \
+       ao_adc_avr.c \
+       ao_pyro_slave.c \
+       ao_spi_slave.c \
+       ao_eeprom_avr.c \
+       ao_storage.c \
+       ao_config.c \
+       ao_pyro.c
+
+PRODUCT=TelePyro-v0.1
+MCU=atmega32u4
+PRODUCT_DEF=-DTELEPYRO
+IDPRODUCT=0x0011
+CFLAGS = $(PRODUCT_DEF) -I. -I../avr -I../core -I..
+CFLAGS += -mmcu=$(MCU) -Wall -Wstrict-prototypes -O3 -mcall-prologues -DAVR
+
+NICKLE=nickle
+
+PROG=telepyro-v0.1
+
+SRC=$(ALTOS_SRC) ao_telepyro.c
+OBJ=$(SRC:.c=.o)
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+CHECK=sh ../util/check-avr-mem
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ)
+       $(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1)
+
+$(PROG).hex: $(PROG)
+       avr-size $(PROG)
+       $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@
+
+
+load: $(PROG).hex
+       $(LOADCMD) $(LOADARG)$(PROG).hex
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+ao_product.o: ao_product.c ao_product.h
+
+%.o : %.c
+       $(call quiet,CC) -c $(CFLAGS) $<
+
+distclean:     clean
+
+clean:
+       rm -f $(OBJ)
+       rm -f ao_product.h
+
+install:
+
+uninstall:
+
+$(OBJ): $(INC)
diff --git a/src/telescience-v0.1/.gitignore b/src/telescience-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..dfccadf
--- /dev/null
@@ -0,0 +1,2 @@
+telescience-v0.1*
+ao_product.h
diff --git a/src/telescience-v0.1/Makefile b/src/telescience-v0.1/Makefile
new file mode 100644 (file)
index 0000000..d24128e
--- /dev/null
@@ -0,0 +1,115 @@
+#
+# AltOS build
+#
+#
+vpath % ..:../core:../product:../drivers:../avr
+vpath ao-make-product.5c ../util
+
+MCU=atmega32u4
+DUDECPUTYPE=m32u4
+#PROGRAMMER=stk500v2 -P usb
+PROGRAMMER=usbtiny
+LOADCMD=avrdude
+LOADARG=-p $(DUDECPUTYPE) -c $(PROGRAMMER) -e -U flash:w:
+CC=avr-gcc
+OBJCOPY=avr-objcopy
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_usb.h \
+       ao_pins.h \
+       ao_product.h
+
+#
+# Common AltOS sources
+#
+TELESCIENCE_STORAGE= \
+       ao_m25.c \
+       ao_spi_usart.c \
+       ao_storage.c
+
+TELESCIENCE_LOG= \
+       ao_log_single.c \
+       ao_log_telescience.c
+
+ALTOS_SRC = \
+       ao_clock.c \
+       ao_cmd.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_product.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_timer.c \
+       ao_led.c \
+       ao_avr_stdio.c \
+       ao_romconfig.c \
+       ao_usb_avr.c \
+       ao_adc_avr.c \
+       ao_science_slave.c \
+       ao_spi_slave.c \
+       $(TELESCIENCE_STORAGE)\
+       $(TELESCIENCE_LOG)
+
+PRODUCT=TeleScience-v0.1
+MCU=atmega32u4
+PRODUCT_DEF=-DTELESCIENCE
+IDPRODUCT=0x0011
+CFLAGS = $(PRODUCT_DEF) -I. -I../avr -I../core -I..
+CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O3 -mcall-prologues -DAVR
+
+NICKLE=nickle
+
+PROG=telescience-v0.1
+
+SRC=$(ALTOS_SRC) ao_telescience.c
+OBJ=$(SRC:.c=.o)
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: $(PROG)
+
+CHECK=sh ../util/check-avr-mem
+
+$(PROG): Makefile $(OBJ)
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ)
+       $(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1)
+
+$(PROG).hex: $(PROG)
+       avr-size $(PROG)
+       $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@
+
+
+load: $(PROG).hex
+       $(LOADCMD) $(LOADARG)$(PROG).hex
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+ao_product.o: ao_product.c ao_product.h
+
+%.o : %.c $(INC)
+       $(call quiet,CC) -c $(CFLAGS) $<
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROG) $(PROG).hex
+       rm -f ao_product.h
+
+install:
+
+uninstall:
+
+$(OBJ): ao_product.h $(INC)
diff --git a/src/teleshield-v0.1/.gitignore b/src/teleshield-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..3ae78d3
--- /dev/null
@@ -0,0 +1,2 @@
+teleshield-*
+ao_product.h
diff --git a/src/teleshield-v0.1/Makefile b/src/teleshield-v0.1/Makefile
new file mode 100644 (file)
index 0000000..ab2a025
--- /dev/null
@@ -0,0 +1,111 @@
+#
+# TeleShield build file
+#
+# The various telemetrum versions differ only
+# in which flash and GPS drivers are included,
+# so the per-board makefiles simply define
+# TM_VER, TM_DEF, TM_INC and TM_SRC and include
+# this file
+
+TELESHIELD_VER=0.1
+TELESHIELD_DEF=0_1
+
+TELESHIELD_INC =
+
+TELESHIELD_SRC = \
+       ao_beep.c \
+       ao_btm.c \
+       ao_spi.c
+
+vpath %.c ..:../core:../cc1111:../drivers:../product:.
+vpath %.h ..:../core:../cc1111:../drivers:../product:.
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       cc1111.h \
+       ao_product.h \
+       $(TELESHIELD_INC)
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_monitor.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_state.c \
+       ao_storage.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_led.c \
+       ao_intflash.c \
+       ao_packet.c \
+       ao_packet_slave.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC = \
+       $(TELESHIELD_SRC)
+
+PRODUCT_SRC = \
+       ao_teleshield.c \
+       ao_ardu_serial.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = teleshield-v$(TELESHIELD_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleShield-v$(TELESHIELD_VER)
+PRODUCT_DEF=-DTELESHIELD_V_$(TELESHIELD_DEF)
+IDPRODUCT=0x0013
+CODESIZE=0x7800
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
+
diff --git a/src/teleshield-v0.1/ao_ardu_serial.c b/src/teleshield-v0.1/ao_ardu_serial.c
new file mode 100644 (file)
index 0000000..e6e19f6
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+static void
+ao_ardu_serial_recv(void)
+{
+       char    c;
+
+       for (;;) {
+               if (ao_fifo_empty(ao_serial0_rx_fifo))
+                       flush();
+               c = ao_serial0_getchar();
+               putchar (c);
+       }
+}
+
+static __xdata struct ao_task ao_ardu_serial_recv_task;
+
+void
+ao_ardu_serial_init (void)
+{
+       ao_add_task(&ao_ardu_serial_recv_task, ao_ardu_serial_recv, "recv");
+}
diff --git a/src/teleshield-v0.1/ao_pins.h b/src/teleshield-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..701e25f
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#if defined(TELESHIELD_V_0_1)
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                0
+       #define HAS_SERIAL_1            1
+       #define HAS_SERIAL_1_ALT_1      1
+       #define HAS_SERIAL_1_ALT_2      0
+       #define HAS_SERIAL_1_HW_FLOW    1
+       #define USE_SERIAL_1_STDIN      1
+       #define HAS_SERIAL_0            1
+       #define HAS_SERIAL_0_ALT_1      0
+       #define HAS_SERIAL_0_ALT_2      1
+       #define HAS_SERIAL_0_HW_FLOW    0
+       #define HAS_ADC                 0
+       #define HAS_DBG                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 0
+       #define USE_INTERNAL_FLASH      1
+       #define HAS_BTM                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       0
+       #define PACKET_HAS_SLAVE        1
+       #define AO_LED_RED              1
+       #define AO_LED_GREEN            2
+       #define AO_MONITOR_LED          AO_LED_RED
+       #define LEDS_AVAILABLE          (AO_LED_RED|AO_LED_GREEN)
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define HAS_ACCEL               0
+       #define HAS_IGNITE              0
+       #define HAS_IGNITE_REPORT       0
+       #define BT_LINK_ON_P2           0
+       #define BT_LINK_ON_P1           1
+       #define BT_LINK_PIN_INDEX       7
+       #define BT_LINK_PIN             P1_7
+       #define HAS_MONITOR             1
+       #define LEGACY_MONITOR          0
+       #define HAS_RSSI                0
+       #define HAS_AES                 0
+#endif
+
+#if DBG_ON_P1
+
+       #define DBG_CLOCK       (1 << 4)        /* mi0 */
+       #define DBG_DATA        (1 << 5)        /* mo0 */
+       #define DBG_RESET_N     (1 << 3)        /* c0 */
+
+       #define DBG_CLOCK_PIN   (P1_4)
+       #define DBG_DATA_PIN    (P1_5)
+       #define DBG_RESET_N_PIN (P1_3)
+
+       #define DBG_PORT_NUM    1
+       #define DBG_PORT        P1
+       #define DBG_PORT_SEL    P1SEL
+       #define DBG_PORT_INP    P1INP
+       #define DBG_PORT_DIR    P1DIR
+
+#endif /* DBG_ON_P1 */
+
+#if DBG_ON_P0
+
+       #define DBG_CLOCK       (1 << 3)
+       #define DBG_DATA        (1 << 4)
+       #define DBG_RESET_N     (1 << 5)
+
+       #define DBG_CLOCK_PIN   (P0_3)
+       #define DBG_DATA_PIN    (P0_4)
+       #define DBG_RESET_N_PIN (P0_5)
+
+       #define DBG_PORT_NUM    0
+       #define DBG_PORT        P0
+       #define DBG_PORT_SEL    P0SEL
+       #define DBG_PORT_INP    P0INP
+       #define DBG_PORT_DIR    P0DIR
+
+#endif /* DBG_ON_P0 */
+
+#if COMPANION_CS_ON_P1
+       #define COMPANION_CS_PORT       P1
+       #define COMPANION_CS_SEL        P1SEL
+       #define COMPANION_CS_DIR        P1DIR
+#endif
+
+#if SPI_CS_ON_P1
+       #define SPI_CS_PORT     P1
+       #define SPI_CS_SEL      P1SEL
+       #define SPI_CS_DIR      P1DIR
+#endif
+
+#if SPI_CS_ON_P0
+       #define SPI_CS_PORT     P0
+       #define SPI_CS_SEL      P0SEL
+       #define SPI_CS_DIR      P0DIR
+#endif
+
+#ifndef IGNITE_ON_P2
+#error Please define IGNITE_ON_P2
+#endif
+
+#ifndef IGNITE_ON_P0
+#error Please define IGNITE_ON_P0
+#endif
+
+#ifndef HAS_SERIAL_1
+#error Please define HAS_SERIAL_1
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#ifndef HAS_EEPROM
+#error Please define HAS_EEPROM
+#endif
+
+#ifndef HAS_LOG
+#error Please define HAS_LOG
+#endif
+
+#if HAS_EEPROM
+#ifndef USE_INTERNAL_FLASH
+#error Please define USE_INTERNAL_FLASH
+#endif
+#endif
+
+#ifndef HAS_DBG
+#error Please define HAS_DBG
+#endif
+
+#ifndef HAS_IGNITE
+#error Please define HAS_IGNITE
+#endif
+
+#if HAS_IGNITE
+#define HAS_IGNITE_REPORT 1
+#endif
+
+#ifndef PACKET_HAS_MASTER
+#error Please define PACKET_HAS_MASTER
+#endif
+
+#ifndef PACKET_HAS_SLAVE
+#error Please define PACKET_HAS_SLAVE
+#endif
+
+#ifndef HAS_MONITOR
+#error Please define HAS_MONITOR
+#endif
+
+#if HAS_MONITOR
+#ifndef HAS_RSSI
+#error Please define HAS_RSSI
+#endif
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#if HAS_ADC
+
+#if HAS_ACCEL
+#ifndef HAS_ACCEL_REF
+#error Please define HAS_ACCEL_REF
+#endif
+#else
+#define HAS_ACCEL_REF 0
+#endif
+
+#endif /* HAS_ADC */
+
+#if IGNITE_ON_P2
+#define AO_IGNITER_DROGUE      P2_3
+#define AO_IGNITER_MAIN                P2_4
+#define AO_IGNITER_DIR         P2DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 3)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+#if IGNITE_ON_P0
+#define AO_IGNITER_DROGUE      P0_5
+#define AO_IGNITER_MAIN                P0_4
+#define AO_IGNITER_DIR         P0DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 5)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+/* test these values with real igniters */
+#define AO_IGNITER_OPEN                1000
+#define AO_IGNITER_CLOSED      7000
+#define AO_IGNITER_FIRE_TIME   AO_MS_TO_TICKS(50)
+#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
+
+void
+ao_ardu_serial_init (void);
+
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/teleshield-v0.1/ao_teleshield.c b/src/teleshield-v0.1/ao_teleshield.c
new file mode 100644 (file)
index 0000000..4c32817
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+
+#if 0
+__code uint8_t ao_log_format = AO_LOG_FORMAT_NONE;     /* until we actually log stuff */
+#endif
+
+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_spi_init();
+       ao_storage_init();
+       ao_usb_init();
+       ao_monitor_init();
+       ao_radio_init();
+       ao_packet_slave_init(1);
+       ao_btm_init();
+#if HAS_DBG
+       ao_dbg_init();
+#endif
+#if HAS_AES
+       ao_aes_init();
+       ao_radio_cmac_init();
+#endif
+       ao_serial_init();
+       ao_ardu_serial_init();
+       ao_config_init();
+       ao_start_scheduler();
+}
diff --git a/src/teleterra-v0.1/.gitignore b/src/teleterra-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..447007c
--- /dev/null
@@ -0,0 +1,2 @@
+teleterra-v0.1*
+ao_product.h
diff --git a/src/teleterra-v0.1/ao_pins.h b/src/teleterra-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..ba7177a
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#ifdef TELETERRA_V_0_1
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_BEEP                0
+       #define HAS_GPS                 0
+       #define HAS_SERIAL_1            0
+       #define HAS_ADC                 0
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define HAS_DBG                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       1
+       #define PACKET_HAS_SLAVE        0
+
+       #define HAS_COMPANION           1
+       #define COMPANION_CS_ON_P1      1
+       #define COMPANION_CS_MASK       0x4     /* CS1 is P1_2 */
+       #define COMPANION_CS            P1_2
+
+       #define AO_LED_RED              1
+       #define LEDS_AVAILABLE          (AO_LED_RED)
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL_REF           0
+       #define HAS_ACCEL               0
+       #define HAS_IGNITE              0
+       #define HAS_MONITOR             1
+       #define HAS_RSSI                0
+       #define HAS_AES                 1
+
+       #define SPI_CS_ON_P1            0
+       #define SPI_CS_ON_P0            1
+       #define M25_CS_MASK             0xf
+       #define M25_MAX_CHIPS           4
+#endif
+
+#if DBG_ON_P1
+
+       #define DBG_CLOCK       (1 << 4)        /* mi0 */
+       #define DBG_DATA        (1 << 5)        /* mo0 */
+       #define DBG_RESET_N     (1 << 3)        /* c0 */
+
+       #define DBG_CLOCK_PIN   (P1_4)
+       #define DBG_DATA_PIN    (P1_5)
+       #define DBG_RESET_N_PIN (P1_3)
+
+       #define DBG_PORT_NUM    1
+       #define DBG_PORT        P1
+       #define DBG_PORT_SEL    P1SEL
+       #define DBG_PORT_INP    P1INP
+       #define DBG_PORT_DIR    P1DIR
+
+#endif /* DBG_ON_P1 */
+
+#if DBG_ON_P0
+
+       #define DBG_CLOCK       (1 << 3)
+       #define DBG_DATA        (1 << 4)
+       #define DBG_RESET_N     (1 << 5)
+
+       #define DBG_CLOCK_PIN   (P0_3)
+       #define DBG_DATA_PIN    (P0_4)
+       #define DBG_RESET_N_PIN (P0_5)
+
+       #define DBG_PORT_NUM    0
+       #define DBG_PORT        P0
+       #define DBG_PORT_SEL    P0SEL
+       #define DBG_PORT_INP    P0INP
+       #define DBG_PORT_DIR    P0DIR
+
+#endif /* DBG_ON_P0 */
+
+#if COMPANION_CS_ON_P1
+       #define COMPANION_CS_PORT       P1
+       #define COMPANION_CS_SEL        P1SEL
+       #define COMPANION_CS_DIR        P1DIR
+#endif
+
+#if SPI_CS_ON_P1
+       #define SPI_CS_PORT     P1
+       #define SPI_CS_SEL      P1SEL
+       #define SPI_CS_DIR      P1DIR
+#endif
+
+#if SPI_CS_ON_P0
+       #define SPI_CS_PORT     P0
+       #define SPI_CS_SEL      P0SEL
+       #define SPI_CS_DIR      P0DIR
+#endif
+
+#ifndef IGNITE_ON_P2
+#error Please define IGNITE_ON_P2
+#endif
+
+#ifndef IGNITE_ON_P0
+#error Please define IGNITE_ON_P0
+#endif
+
+#ifndef HAS_SERIAL_1
+#error Please define HAS_SERIAL_1
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#ifndef HAS_EEPROM
+#error Please define HAS_EEPROM
+#endif
+
+#ifndef HAS_LOG
+#error Please define HAS_LOG
+#endif
+
+#if HAS_EEPROM
+#ifndef USE_INTERNAL_FLASH
+#error Please define USE_INTERNAL_FLASH
+#endif
+#endif
+
+#ifndef HAS_DBG
+#error Please define HAS_DBG
+#endif
+
+#ifndef HAS_IGNITE
+#error Please define HAS_IGNITE
+#endif
+
+#if HAS_IGNITE
+#define HAS_IGNITE_REPORT 1
+#endif
+
+#ifndef PACKET_HAS_MASTER
+#error Please define PACKET_HAS_MASTER
+#endif
+
+#ifndef PACKET_HAS_SLAVE
+#error Please define PACKET_HAS_SLAVE
+#endif
+
+#ifndef HAS_MONITOR
+#error Please define HAS_MONITOR
+#endif
+
+#if HAS_MONITOR
+#ifndef HAS_RSSI
+#error Please define HAS_RSSI
+#endif
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#if HAS_ADC
+
+#if HAS_ACCEL
+#ifndef HAS_ACCEL_REF
+#error Please define HAS_ACCEL_REF
+#endif
+#else
+#define HAS_ACCEL_REF 0
+#endif
+
+#endif /* HAS_ADC */
+
+#if IGNITE_ON_P2
+#define AO_IGNITER_DROGUE      P2_3
+#define AO_IGNITER_MAIN                P2_4
+#define AO_IGNITER_DIR         P2DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 3)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+#if IGNITE_ON_P0
+#define AO_IGNITER_DROGUE      P0_5
+#define AO_IGNITER_MAIN                P0_4
+#define AO_IGNITER_DIR         P0DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 5)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+/* test these values with real igniters */
+#define AO_IGNITER_OPEN                1000
+#define AO_IGNITER_CLOSED      7000
+#define AO_IGNITER_FIRE_TIME   AO_MS_TO_TICKS(50)
+#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/teleterra-v0.2/.gitignore b/src/teleterra-v0.2/.gitignore
new file mode 100644 (file)
index 0000000..9daebc3
--- /dev/null
@@ -0,0 +1,2 @@
+teleterra-v0.2*
+ao_product.h
diff --git a/src/teleterra-v0.2/.sdcdbrc b/src/teleterra-v0.2/.sdcdbrc
new file mode 100644 (file)
index 0000000..fbe9a59
--- /dev/null
@@ -0,0 +1 @@
+--directory=../cc1111:../product:../core:../drivers:.
diff --git a/src/teleterra-v0.2/Makefile b/src/teleterra-v0.2/Makefile
new file mode 100644 (file)
index 0000000..65db57c
--- /dev/null
@@ -0,0 +1,104 @@
+#
+# TeleTerra build file
+#
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_monitor.c \
+       ao_log_single.c \
+       ao_log_telem.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_report.c \
+       ao_sqrt.c \
+       ao_stdio.c \
+       ao_storage.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_battery.c \
+       ao_beep.c \
+       ao_button.c \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_packet.c \
+       ao_packet_master.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_serial.c \
+       ao_spi.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       ao_lcd_port.c \
+       _bp.c
+
+DRIVER_SRC = \
+       ao_m25.c \
+       ao_lcd.c \
+       ao_gps_skytraq.c
+
+PRODUCT_SRC = \
+       ao_teleterra_0_2.c \
+       ao_terraui.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+TELETERRA_VER=0.2
+TELETERRA_DEF=0_2
+PROGNAME = teleterra-v$(TELETERRA_VER)
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TeleTerra-v$(TELETERRA_VER)
+PRODUCT_DEF=-DTELETERRA_V_$(TELETERRA_DEF)
+IDPRODUCT=0x000d
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
+
diff --git a/src/teleterra-v0.2/ao_pins.h b/src/teleterra-v0.2/ao_pins.h
new file mode 100644 (file)
index 0000000..1c12c43
--- /dev/null
@@ -0,0 +1,232 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+#ifdef TELETERRA_V_0_2
+       #define HAS_FLIGHT              0
+       #define HAS_USB                 1
+       #define HAS_RADIO               1
+       #define HAS_BEEP                1
+       #define HAS_GPS                 1
+       #define HAS_SERIAL_1            1
+       #define HAS_ADC                 0
+       #define HAS_LCD                 1
+       #define HAS_EEPROM              1
+       #define HAS_LOG                 1
+       #define USE_INTERNAL_FLASH      0
+       #define HAS_DBG                 1
+       #define DBG_ON_P1               1
+       #define DBG_ON_P0               0
+       #define IGNITE_ON_P2            0
+       #define IGNITE_ON_P0            0
+       #define PACKET_HAS_MASTER       1
+       #define PACKET_HAS_SLAVE        0
+       #define HAS_RADIO_CHANNELS      1
+
+       #define HAS_COMPANION           0
+
+       #define AO_MONITOR_LED          0
+       #define LEDS_AVAILABLE          0
+       #define HAS_EXTERNAL_TEMP       0
+       #define HAS_ACCEL_REF           0
+       #define HAS_ACCEL               0
+       #define HAS_IGNITE              0
+       #define HAS_MONITOR             1
+       #define HAS_MONITOR_PUT         1
+       #define LEGACY_MONITOR          0
+       #define HAS_RSSI                0
+       #define HAS_AES                 0
+
+       #define SPI_CS_ON_P1            1
+       #define SPI_CS_ON_P0            0
+       #define M25_CS_MASK             0x04
+       #define M25_MAX_CHIPS           1
+
+       #define HAS_BUTTON              1
+       #define BUTTON_1_REG            0
+       #define BUTTON_1_MASK           (1 << 4)        /* P0_4 */
+
+       #define BUTTON_2_REG            2
+       #define BUTTON_2_MASK           (1 << 3)        /* P2_3 */
+
+       #define BUTTON_3_REG            2
+       #define BUTTON_3_MASK           (1 << 4)        /* P2_4 */
+
+       #define HAS_P2_ISR              1
+
+       #define BATTERY_PIN             5
+       
+#endif
+
+#if DBG_ON_P1
+
+       #define DBG_CLOCK       (1 << 4)        /* mi0 */
+       #define DBG_DATA        (1 << 5)        /* mo0 */
+       #define DBG_RESET_N     (1 << 3)        /* c0 */
+
+       #define DBG_CLOCK_PIN   (P1_4)
+       #define DBG_DATA_PIN    (P1_5)
+       #define DBG_RESET_N_PIN (P1_3)
+
+       #define DBG_PORT_NUM    1
+       #define DBG_PORT        P1
+       #define DBG_PORT_SEL    P1SEL
+       #define DBG_PORT_INP    P1INP
+       #define DBG_PORT_DIR    P1DIR
+
+#endif /* DBG_ON_P1 */
+
+#if DBG_ON_P0
+
+       #define DBG_CLOCK       (1 << 3)
+       #define DBG_DATA        (1 << 4)
+       #define DBG_RESET_N     (1 << 5)
+
+       #define DBG_CLOCK_PIN   (P0_3)
+       #define DBG_DATA_PIN    (P0_4)
+       #define DBG_RESET_N_PIN (P0_5)
+
+       #define DBG_PORT_NUM    0
+       #define DBG_PORT        P0
+       #define DBG_PORT_SEL    P0SEL
+       #define DBG_PORT_INP    P0INP
+       #define DBG_PORT_DIR    P0DIR
+
+#endif /* DBG_ON_P0 */
+
+#if COMPANION_CS_ON_P1
+       #define COMPANION_CS_PORT       P1
+       #define COMPANION_CS_SEL        P1SEL
+       #define COMPANION_CS_DIR        P1DIR
+#endif
+
+#if SPI_CS_ON_P1
+       #define SPI_CS_PORT     P1
+       #define SPI_CS_SEL      P1SEL
+       #define SPI_CS_DIR      P1DIR
+#endif
+
+#if SPI_CS_ON_P0
+       #define SPI_CS_PORT     P0
+       #define SPI_CS_SEL      P0SEL
+       #define SPI_CS_DIR      P0DIR
+#endif
+
+#ifndef IGNITE_ON_P2
+#error Please define IGNITE_ON_P2
+#endif
+
+#ifndef IGNITE_ON_P0
+#error Please define IGNITE_ON_P0
+#endif
+
+#ifndef HAS_SERIAL_1
+#error Please define HAS_SERIAL_1
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#ifndef HAS_EEPROM
+#error Please define HAS_EEPROM
+#endif
+
+#ifndef HAS_LOG
+#error Please define HAS_LOG
+#endif
+
+#if HAS_EEPROM
+#ifndef USE_INTERNAL_FLASH
+#error Please define USE_INTERNAL_FLASH
+#endif
+#endif
+
+#ifndef HAS_DBG
+#error Please define HAS_DBG
+#endif
+
+#ifndef HAS_IGNITE
+#error Please define HAS_IGNITE
+#endif
+
+#if HAS_IGNITE
+#define HAS_IGNITE_REPORT 1
+#endif
+
+#ifndef PACKET_HAS_MASTER
+#error Please define PACKET_HAS_MASTER
+#endif
+
+#ifndef PACKET_HAS_SLAVE
+#error Please define PACKET_HAS_SLAVE
+#endif
+
+#ifndef HAS_MONITOR
+#error Please define HAS_MONITOR
+#endif
+
+#if HAS_MONITOR
+#ifndef HAS_RSSI
+#error Please define HAS_RSSI
+#endif
+#endif
+
+#ifndef HAS_ADC
+#error Please define HAS_ADC
+#endif
+
+#if HAS_ADC
+
+#if HAS_ACCEL
+#ifndef HAS_ACCEL_REF
+#error Please define HAS_ACCEL_REF
+#endif
+#else
+#define HAS_ACCEL_REF 0
+#endif
+
+#endif /* HAS_ADC */
+
+#if IGNITE_ON_P2
+#define AO_IGNITER_DROGUE      P2_3
+#define AO_IGNITER_MAIN                P2_4
+#define AO_IGNITER_DIR         P2DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 3)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+#if IGNITE_ON_P0
+#define AO_IGNITER_DROGUE      P0_5
+#define AO_IGNITER_MAIN                P0_4
+#define AO_IGNITER_DIR         P0DIR
+#define AO_IGNITER_DROGUE_BIT  (1 << 5)
+#define AO_IGNITER_MAIN_BIT    (1 << 4)
+#endif
+
+/* test these values with real igniters */
+#define AO_IGNITER_OPEN                1000
+#define AO_IGNITER_CLOSED      7000
+#define AO_IGNITER_FIRE_TIME   AO_MS_TO_TICKS(50)
+#define AO_IGNITER_CHARGE_TIME AO_MS_TO_TICKS(2000)
+
+#define AO_M25_SPI_CS_PORT     SPI_CS_PORT
+#define AO_M25_SPI_CS_MASK     M25_CS_MASK
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/test/.gitignore b/src/test/.gitignore
new file mode 100644 (file)
index 0000000..5d528ab
--- /dev/null
@@ -0,0 +1,9 @@
+ao_flight_test
+ao_flight_test_baro
+ao_flight_test_accel
+ao_gps_test
+ao_gps_test_skytraq
+ao_convert_test
+ao_convert_pa_test
+ao_fec_test
+ao_flight_test_noisy_accel
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644 (file)
index 0000000..db3cc04
--- /dev/null
@@ -0,0 +1,47 @@
+vpath % ..:../core:../drivers:../util
+
+PROGS=ao_flight_test ao_flight_test_baro ao_flight_test_accel ao_flight_test_noisy_accel ao_gps_test ao_gps_test_skytraq ao_convert_test ao_convert_pa_test ao_fec_test
+
+KALMAN=make-kalman 
+
+CFLAGS=-I.. -I. -I../core -I../drivers -O3 -g -Wall
+
+all: $(PROGS)
+
+clean:
+       rm -f $(PROGS) run-out.baro run-out.full
+
+install:
+
+ao_flight_test: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+       cc $(CFLAGS) -o $@ $<
+
+ao_flight_test_noisy_accel: ao_flight_test.c ao_host.h ao_flight.c ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+       cc -DNOISY_ACCEL=1 $(CFLAGS) -o $@ $<
+
+ao_flight_test_baro: ao_flight_test.c ao_host.h ao_flight.c  ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+       cc $(CFLAGS) -o $@ -DHAS_ACCEL=0 ao_flight_test.c
+
+ao_flight_test_accel: ao_flight_test.c ao_host.h ao_flight.c  ao_sample.c ao_kalman.c altitude.h ao_kalman.h
+       cc $(CFLAGS) -o $@ -DFORCE_ACCEL=1 ao_flight_test.c
+
+ao_gps_test: ao_gps_test.c ao_gps_sirf.c ao_gps_print.c ao_host.h
+       cc $(CFLAGS) -o $@ $<
+
+ao_gps_test_skytraq: ao_gps_test_skytraq.c ao_gps_skytraq.c ao_gps_print.c ao_host.h
+       cc $(CFLAGS) -o $@ $<
+
+ao_convert_test: ao_convert_test.c ao_convert.c altitude.h
+       cc $(CFLAGS) -o $@ $<
+
+ao_convert_pa_test: ao_convert_pa_test.c ao_convert_pa.c altitude-pa.h
+       cc $(CFLAGS) -o $@ $<
+
+ao_kalman.h: $(KALMAN)
+       (cd .. && make ao_kalman.h)
+
+ao_fec_test: ao_fec_test.c ao_fec_tx.c ao_fec_rx.c
+       cc $(CFLAGS) -DAO_FEC_DEBUG=1 -o $@ ao_fec_test.c ../core/ao_fec_tx.c ../core/ao_fec_rx.c -lm
+
+check: ao_fec_test ao_flight_test ao_flight_test_baro run-tests
+       ./ao_fec_test && ./run-tests
\ No newline at end of file
diff --git a/src/test/ao_fec_test.c b/src/test/ao_fec_test.c
new file mode 100644 (file)
index 0000000..216a4b7
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include <ao_fec.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+#ifndef RANDOM_MAX
+#define RANDOM_MAX 0x7fffffff
+#endif
+
+static double
+rand_real(void) {
+       return (double) random() / (double) RANDOM_MAX;
+}
+
+static double
+gaussian_random(double mean, double dev)
+{
+       static int      save_x_valid = 0;
+       static double   save_x;
+       double          x;
+
+       if (save_x_valid)
+       {
+               x = save_x;
+               save_x_valid = 0;
+       }
+       else
+       {
+               double    w;
+               double    normal_x1, normal_x2;
+
+               do {
+                       normal_x1 = 2 * rand_real () - 1;
+                       normal_x2 = 2 * rand_real () - 1;
+                       w = normal_x1*normal_x1 + normal_x2*normal_x2;
+               } while (w >= 1 || w < 1E-30);
+
+               w = sqrt(log(w)*(-2./w));
+
+               /*
+                * normal_x1 and normal_x2 are independent normally
+                * distributed variates
+                */
+
+               x = normal_x1 * w;
+               /* save normal_x2 for next call */
+               save_x = normal_x2 * w;
+               save_x_valid = 1;
+       }
+       return x * dev + mean;
+}
+
+#define PREPARE_LEN(input_len)         ((input_len) + AO_FEC_PREPARE_EXTRA)
+#define ENCODE_LEN(input_len)          (PREPARE_LEN(input_len) * 2)
+#define DECODE_LEN(input_len)          ((input_len) + AO_FEC_PREPARE_EXTRA)
+#define EXPAND_LEN(input_len)          (ENCODE_LEN(input_len) * 8)
+
+static uint8_t ao_bit(uint8_t b) {
+       if (b)
+               return 0x00;
+       return 0xff;
+}
+
+static int
+ao_expand(uint8_t *bits, int bits_len, uint8_t *bytes)
+{
+       int     i, bit;
+       uint8_t b;
+
+       for (i = 0; i < bits_len; i++) {
+               b = bits[i];
+               for (bit = 7; bit >= 0; bit--)
+                       *bytes++ = ao_bit ((b >> bit) & 1);
+       }
+
+       return bits_len * 8;
+}
+
+static int
+ao_fuzz (uint8_t *in, int in_len, uint8_t *out, double dev)
+{
+       int     i;
+       int     errors = 0;
+       int     s;
+       
+       for (i = 0; i < in_len; i++) {
+               double  error = gaussian_random(0, dev);
+               uint8_t byte = in[i];
+
+               if (error > 0) {
+                       if (error > 0xff)
+                               error = 0xff;
+                       if (error >= 0x80)
+                               errors++;
+                       if (byte < 0x80)
+                               byte += error;
+                       else
+                               byte -= error;
+               }
+
+               /* abcd efgh    8
+                * abcd efga    7
+                * abcd efab    6
+                * abcd eabc    5
+                * abcd abcd    4
+                * abca bcab    3
+                * abab abab    2
+                * aaaa aaaa    1
+                */
+
+#define SAVE           8
+#define SAVE_MASK      (((1 << SAVE) - 1) << (8 - SAVE))
+
+               byte &= SAVE_MASK;
+               for (s = SAVE; s < 8; s += SAVE)
+                       byte |= byte >> s;
+
+               out[i] = byte;
+       }
+       return errors;
+}
+
+static uint8_t
+ao_random_data(uint8_t *out, uint8_t out_len)
+{
+       uint8_t len = random() % (out_len + 1);
+       uint8_t i;
+       
+       for (i = 0; i < len; i++)
+               out[i] = random();
+       return len;
+}      
+
+
+static uint8_t real_packet[] = {
+ 0x40, 0x38, 0xcd, 0x38, 0x3d, 0x34, 0xca, 0x31, 0xc3, 0xc1, 0xc6, 0x35, 0xcc, 0x3a, 0x3c,
+ 0x3c, 0x3d, 0x3c, 0x37, 0xc5, 0xc1, 0xc0, 0xc1, 0xc1, 0xc3, 0xc0, 0xc1, 0xc6, 0x38, 0x3b, 0xc6,
+ 0xc0, 0xc6, 0x32, 0xc9, 0xc9, 0x34, 0xcf, 0x35, 0xcf, 0x3a, 0x3b, 0xc6, 0xc7, 0x35, 0xcf, 0x36,
+ 0xce, 0x37, 0xc8, 0xc8, 0x3a, 0x3c, 0xc9, 0xc8, 0x3a, 0x3c, 0xcc, 0x32, 0xcd, 0x32, 0xce, 0x32,
+ 0xc9, 0xc6, 0x37, 0x3e, 0x3d, 0xc8, 0xc3, 0xc6, 0x38, 0x3d, 0xcf, 0x34, 0x3d, 0x3b, 0xcb, 0x31,
+ 0xca, 0x35, 0x38, 0xcb, 0x31, 0xcc, 0x31, 0x3b, 0xc7, 0xbf, 0xbe, 0xbe, 0xc3, 0x35, 0x38, 0xcb,
+ 0x2f, 0xc9, 0xc2, 0x34, 0x3c, 0xcc, 0x31, 0xca, 0xc1, 0xc0, 0xc3, 0x33, 0x3f, 0x3f, 0x3e, 0x3c,
+ 0xcc, 0x31, 0xcc, 0xc3, 0xc4, 0x32, 0xd1, 0x34, 0x3f, 0x3c, 0xcc, 0x31, 0xcc, 0xc3, 0x33, 0xd0,
+ 0x31, 0xd1, 0x35, 0x3f, 0x3e, 0x3c, 0xca, 0xc5, 0x34, 0xcc, 0xc2, 0xc3, 0xc3, 0xc5, 0x36, 0x40,
+ 0x3e, 0x41, 0x3e, 0x41, 0x3e, 0x3f, 0x41, 0x40, 0xce, 0xc5, 0x33, 0xcf, 0xc7, 0x36, 0xd2, 0x32,
+ 0xd1, 0xc5, 0xc6, 0x35, 0xd4, 0x32, 0xd1, 0xc6, 0x34, 0x41, 0xd2, 0x30, 0xd0, 0xc2, 0xc2, 0xc5,
+ 0x31, 0xd3, 0x31, 0xd1, 0xc7, 0x33, 0xd4, 0xc6, 0x34, 0x42, 0x40, 0x40, 0x40, 0x41, 0x41, 0xd2,
+ 0xc4, 0xc4, 0x39, 0x3e, 0xce, 0x35, 0x3c, 0xcd, 0x31, 0x3d, 0xc8, 0xc3, 0x32, 0x3d, 0xcc, 0x2f,
+ 0xce, 0x30, 0xd0, 0x2e, 0xcf, 0x30, 0x3d, 0x3c, 0x3c, 0x3a, 0xcb, 0xbf, 0xbf, 0xc1, 0x31, 0x3d,
+ 0x3d, 0x3c, 0x3c, 0x3c, 0x3d, 0x3b, 0xcf, 0x2e, 0xd2, 0x2e, 0xd0, 0x2c, 0x3b, 0xd1, 0x2a, 0xcf,
+ 0xbe, 0xc0, 0xc0, 0xbf, 0xc1, 0xc1, 0x2c, 0x3f, 0xd0, 0xc0, 0x2e, 0xd0, 0x31, 0xd2, 0xc2, 0x2f,
+ 0x3e, 0x3e, 0x3d, 0xcf, 0x31, 0xcb, 0xc3, 0x32, 0xd1, 0xc4, 0x37, 0x3f, 0xce, 0xc4, 0x35, 0xd0,
+ 0x33, 0x3f, 0xce, 0x31, 0x3c, 0x3b, 0xcc, 0x31, 0xcd, 0x2f, 0xd1, 0x32, 0x3a, 0x3b, 0xcb, 0xc1,
+ 0xc0, 0x32, 0x3e, 0x3e, 0x3c, 0x3d, 0x3d, 0xce, 0x2d, 0xcd, 0xc3, 0xc2, 0x30, 0x3d, 0xcf, 0xc1,
+ 0xc2, 0x30, 0xd0, 0xc4, 0x31, 0xd4, 0x30, 0x40, 0x3e, 0xd0, 0x2f, 0xd0, 0x2f, 0xd1, 0xc2, 0x2e,
+ 0xd4, 0x2e, 0x3f, 0xce, 0xc2, 0x34, 0x3e, 0x3f, 0xd0, 0x30, 0xcf, 0x31, 0x3d, 0x3d, 0xcc, 0x2d,
+ 0xcf, 0x2f, 0xcf, 0x2e, 0x3b, 0xcf, 0x2c, 0x3b, 0x3b, 0x3a, 0x3d, 0x38, 0xcc, 0x2d, 0x3b, 0xcc,
+ 0xbe, 0x2d, 0xd1, 0x2c, 0x3c, 0x3d, 0xce, 0xc0, 0x2d, 0xd1, 0x2f, 0x3f, 0x3f, 0x3c, 0x3f, 0x3e,
+ 0x3e, 0x3c, 0xd3, 0x2a, 0xd1, 0xc2, 0x2e, 0x3c, 0xd1, 0x2f, 0x3d, 0xd1, 0xbe, 0xc1, 0x2d, 0xd2,
+ 0x2d, 0x3d, 0x3d, 0x3b, 0xcd, 0x31, 0xcc, 0x31, 0xce, 0x2e, 0x3d, 0x3e, 0x3a, 0xcd, 0x2d, 0xcc,
+ 0xc1, 0xc1, 0xc3, 0x2e, 0x3f, 0x3f, 0x3c, 0xcf, 0xc0, 0x31, 0xd1, 0xc2, 0xc3, 0x33, 0xd2, 0xc7,
+ 0x32, 0x40, 0xd3, 0xc4, 0xc4, 0x33, 0x40, 0x40, 0xd2, 0x2f, 0xd0, 0x2f, 0x3c, 0xd1, 0x2c, 0x3a,
+ 0x3d, 0xd0, 0x2a, 0xd0, 0x28, 0xcf, 0xc3, 0x2c, 0xd2, 0x2d, 0xd3, 0xc3, 0xc3, 0x2e, 0x3e, 0x41,
+ 0xd2, 0xc3, 0xc2, 0xc2, 0xc2, 0xc2, 0xc4, 0x32, 0xd2, 0x34, 0x3e, 0x3e, 0x3c, 0xd1, 0x30, 0x3d,
+ 0x3c, 0xce, 0x2e, 0x3b, 0x38, 0xcb, 0xbe, 0xc1, 0x2e, 0x3e, 0x3c, 0x3d, 0xd1, 0x2c, 0x3b, 0x3b,
+ 0xcd, 0xbf, 0x2d, 0xd0, 0x2f, 0xd0, 0x2d, 0xd1, 0x2d, 0x3d, 0xd0, 0x2b, 0x3b, 0xcf, 0x2b, 0x3a,
+ 0xcc, 0xbc, 0xc1, 0x2a, 0x3c, 0xce, 0x28, 0xd1, 0x2a, 0x3c, 0x3a, 0xcf, 0xbf, 0x2b, 0x3e, 0x3c,
+ 0xd2, 0x2b, 0x3d, 0x3a, 0x3a, 0xcf, 0x2c, 0xcc, 0xbe, 0xc1, 0xc0, 0xbf, 0xc1, 0x31, 0x3c, 0xce,
+ 0xc0, 0xc1, 0xc0, 0xc1, 0x31, 0x3c, 0xd1, 0xc2, 0x2e, 0xd1, 0xc3, 0xc4, 0x30, 0x3f, 0xd3, 0x2c,
+ 0xd3, 0xc2, 0x30, 0xd5, 0xc3, 0xc5, 0x30, 0xd5, 0xc4, 0xc5, 0xc5, 0x31, 0x41, 0xd4, 0xc4, 0x31,
+ 0x40, 0xd4, 0x2d, 0xd5, 0xc0, 0xc3, 0x2c, 0x3f, 0x3e, 0x3f, 0x3f, 0x3f, 0xd6, 0x2c, 0x3e, 0xd2,
+ 0x27, 0x37, 0xde, 0xc9, 0xe9, 0x2e, 0xc7, 0x3c, 0xd9, 0x07, 0xf3, 0x28, 0x8d, 0xa5, 0xc9, 0xca,
+ 0xe7, 0xcb, 0xfc, 0xb3, 0x3c, 0xd4, 0xd3, 0x9a, 0xe7, 0x2c, 0xc8, 0xf8, 0x44, 0xb3, 0xb0, 0xfa,
+ 0x1c, 0xbf, 0xc9, 0xff, 0xbb, 0x1d, 0x02, 0xdb, 0x45, 0xc1, 0x40, 0x1c, 0xec, 0x48, 0xda, 0x21,
+ 0x4c, 0xe3, 0x38, 0xdf, 0x34, 0xd8, 0x35, 0xd8, 0x31, 0xd7, 0x30, 0xd4, 0x2f, 0xd6, 0x2d, 0xd5,
+ 0x2b, 0xd5, 0x33, 0xce, 0x33, 0xce, 0x33, 0xd0, 0x34, 0xcf, 0x31, 0xcf, 0x30, 0xce, 0x30, 0xcf,
+ 0x30, 0x3d, 0xce, 0x2f, 0xcf, 0xc2, 0x30, 0x3f, 0x3d, 0xcf, 0xc1, 0x31, 0xd0, 0xc5, 0xc4, 0x33,
+ 0x41, 0x3e, 0xd1, 0x30, 0x3c, 0x3d, 0xce, 0x2f, 0xce, 0xc1, 0xc3, 0x2e, 0xd3, 0x30, 0x3e, 0x3e,
+ 0x3c, 0x3f, 0x3c, 0xd1, 0xc0, 0xc0, 0xc0, 0xc1, 0xc2, 0xc0, 0xc1, 0xc3, 0x2c, 0x3f, 0xd2, 0xbe,
+ 0xc1, 0x2e, 0xcf, 0xc4, 0x33, 0xd3, 0x34, 0xd0, 0x33, 0x3d, 0xcf, 0xc3, 0x32, 0xce, 0x33, 0xd2,
+ 0x32, 0xce, 0xc5, 0x34, 0x40, 0xce, 0xc5, 0x32, 0x3e, 0xd0, 0x31, 0xd0, 0x2f, 0xd2, 0x30, 0xce,
+ 0xc2, 0x33, 0x40, 0x3e, 0xd0, 0xc3, 0xc2, 0x2e, 0xd3, 0x31, 0xd0, 0xc7, 0x32, 0x40, 0x3e, 0xd3,
+ 0x2f, 0xd3, 0x2e, 0xd0, 0x2d, 0xd3, 0x2d, 0xd3, 0x2f, 0x3c, 0x3d, 0xd1, 0x2a, 0x3b, 0xd1, 0x28,
+ 0x3b, 0x3b, 0xc8, 0x31, 0x39, 0x3b, 0x38, 0xc6, 0xbf, 0x31, 0x3d, 0x3a, 0xca, 0xc0, 0x33, 0x3c,
+ 0xca, 0xc0, 0xc1, 0xc1, 0xc2, 0x35, 0x3e, 0x3c, 0x3d, 0x3f, 0xcc, 0xc0, 0xc3, 0x31, 0xce, 0xc5,
+ 0x33, 0xd0, 0xc4, 0x35, 0xd3, 0x33, 0xd3, 0x32, 0xd1, 0xc4, 0xc3, 0x35, 0xd3, 0xc6, 0xc4, 0x35,
+ 0xd2, 0xc6, 0x35, 0x41, 0x41, 0xd4, 0x33, 0xd1, 0xc4, 0x30, 0x41, 0xd2, 0x30, 0x3e, 0xce, 0xc1,
+ 0xc3, 0xc0, 0x31, 0xce, 0xc5, 0x34, 0x40, 0x3d, 0xd1, 0xc4, 0x32, 0x3e, 0xcf, 0xc2, 0xc3, 0x30,
+ 0xd4, 0x32, 0x3e, 0x3f, 0x3e, 0x3f, 0x3e, 0xd0, 0xc2, 0x31, 0x3e, 0x3f, 0x3f, 0xd1, 0xc2, 0x2f,
+ 0xd5, 0x2e, 0xd3, 0xc3, 0x2f, 0xd7, 0x30, 0x3e, 0xd2, 0x2c, 0x3e, 0x3c, 0xd0, 0xc1, 0xc1, 0xc3,
+ 0xc0, 0xc3, 0x2c, 0xd4, 0xc2, 0x2e, 0x40, 0xd2, 0xc3, 0x2d, 0xd5, 0xc3, 0x2f, 0x42, 0x40, 0xd6,
+ 0x2d, 0xd4, 0xc2, 0xc2, 0xc2, 0x32, 0xd4, 0xc3, 0xc5, 0xc4, 0x32, 0x40, 0xd3, 0x30, 0xd0, 0xc3,
+ 0xc4, 0x30, 0xd3, 0xc5, 0xc5, 0xc4, 0xc4, 0x33, 0x40, 0x40, 0xd4, 0x2f, 0xd2, 0x2d, 0x3d, 0xd1,
+ 0xc1, 0xc2, 0x2c, 0xd4, 0x2e, 0xd4, 0x2d, 0x3d, 0xd3, 0xc1, 0xc1, 0xc1, 0x2d, 0xd5, 0xc2, 0xc2,
+ 0xc2, 0x2d, 0xd9, 0xc4, 0xc5, 0x2f, 0x43, 0x40, 0xd6, 0xd7, 0xd7, 0x2b, 0x3e, 0xd5, 0x29, 0x3d,
+ 0xd4, 0x24, 0x3b, 0x3a, 0x3b, 0xce, 0x2a, 0x3a, 0xcc, 0xbe, 0x2d, 0x3b, 0x3b, 0x3d, 0x3b, 0x3b,
+ 0xce, 0x2b, 0x3a, 0xcc, 0x2b, 0xd0, 0x2b, 0x3b, 0x3b, 0x39, 0xcf, 0xbf, 0x2a, 0xd1, 0xc0, 0x2f,
+ 0x3e, 0xd0, 0x2d, 0x3a, 0xd1, 0xbe, 0x2b, 0xd2, 0xc3, 0xc2, 0xc0, 0x2d, 0xd7, 0x2c, 0xd7, 0xc2,
+ 0xc3, 0x2f, 0x43, 0x41, 0x40, 0xd4, 0xc3, 0xc3, 0xc3, 0xc2, 0x2c, 0x40, 0xd7, 0x2a, 0x3d, 0xd3,
+ 0x26, 0xd5, 0x2f, 0x3a, 0x3c, 0xca, 0xc3, 0x2c, 0xd3, 0x2e, 0x3c, 0x3d, 0x3d, 0x3d, 0xd0, 0xc2,
+ 0x2e, 0xd3, 0x2e, 0xd1, 0xc5, 0x2e, 0x3f, 0xd3, 0x2e, 0x3c, 0xd1, 0xc0, 0xc2, 0x2a, 0xd2, 0xc2,
+ 0xc4, 0x2e, 0xd7, 0x2e, 0x3e, 0xd3, 0xc1, 0xc3, 0xc1, 0x2d, 0x3f, 0xd3, 0xc2, 0x2c, 0x3f, 0xd3,
+ 0x2b, 0x3b, 0xd1, 0xbf, 0xc1, 0xbe, 0x29, 0x3f, 0xd4, 0x29, 0xd5, 0xbf, 0x29, 0xd7, 0xc0, 0x2d,
+ 0xd8, 0xc2, 0x33, 0x41, 0xd3, 0x30, 0x3d, 0xd0, 0x2c, 0xcf, 0xc1, 0xc1, 0xc2, 0x2e, 0x3f, 0xd3,
+ 0x2b, 0xcf, 0xc3, 0x2f, 0x40, 0x3f, 0x3e, 0xd3, 0x2a, 0x3d, 0x3d, 0xce, 0xc1, 0x2b, 0x3d, 0xd0,
+ 0x2b, 0xd3, 0x2b, 0x3b, 0x3d, 0xce, 0x29, 0x3b, 0x3b, 0x3b, 0xd1, 0xbe, 0x2b, 0xd2, 0x29, 0x3d,
+ 0x3a, 0xd3, 0x29, 0x3a, 0x3b, 0x3b, 0xd2, 0x26, 0x3b, 0xd0, 0xbf, 0xbe, 0xbc, 0xbf, 0xbe, 0xbc,
+ 0x27, 0x3d, 0x3a, 0x3c, 0xce, 0xc1, 0x2c, 0xd2, 0xc2, 0xc2, 0x2f, 0x40, 0xd2, 0xc2, 0xc2, 0x2e,
+ 0x40, 0xd4, 0x2a, 0x3b, 0xcf, 0x2b, 0xd2, 0x2a, 0x3c, 0xd0, 0xc1, 0x2a, 0x3d, 0xd0, 0xc1, 0xc0,
+ 0xbe, 0x2b, 0x3f, 0x3e, 0xd2, 0xc1, 0xc0, 0xc1, 0xc0, 0xc2, 0x2a, 0xd6, 0xc2, 0xc3, 0xc3, 0xc3,
+ 0x2c, 0x3e, 0x41, 0xd6, 0xc0, 0xc3, 0xc2, 0xc2, 0x2a, 0xd9, 0x28, 0x3f, 0xd5, 0xc1, 0xc2, 0xc0,
+ 0xc3, 0x28, 0xd5, 0xc2, 0xc5, 0x30, 0xd6, 0xc4, 0xc5, 0xc5, 0x31, 0x43, 0xd4, 0xc3, 0xc5, 0xc2,
+ 0xc2, 0x2f, 0xd6, 0xc5, 0xc2, 0x2d, 0x41, 0x41, 0x43, 0x40, 0x41, 0xd8, 0x2a, 0x3f, 0x3f, 0x3e,
+ 0x2c, 0x0a, 0x0d, 0x2a, 0xbb, 0x91, 0x5e, 0x2a, 0xca, 0x23, 0xf0, 0x0f, 0xbe, 0xd6, 0xfb, 0xc0,
+ 0x2c, 0x34, 0x7f, 0xd1, 0xc9, 0xc1, 0x1c, 0x06, 0x97, 0x1f, 0x21, 0xa8, 0x04, 0x5d, 0x07, 0xb0,
+ 0x49, 0xaf, 0x10, 0x60, 0xd1, 0xf1, 0xf3, 0xcb, 0x72, 0x16, 0x24, 0xe9, 0xd4, 0x28, 0xb2, 0x8c,
+ 0xf3, 0x3b, 0xe8, 0x06, 0xeb, 0x09, 0xec, 0x0a, 0xec, 0x0b, 0xee, 0x0b, 0xee, 0x0b, 0xf0, 0x0b,
+ 0xf0, 0x0a, 0xf3, 0x0b, 0xf2, 0x0a, 0xf5, 0x08, 0xf5, 0x0a, 0xf4, 0x08, 0xf5, 0x08, 0xf5, 0x09,
+ 0xf4, 0x07, 0x3f, 0xf5, 0x05, 0xf6, 0xbf, 0x06, 0x3f, 0x3f, 0xf8, 0xbe, 0x07, 0xf8, 0xc3, 0xc3,
+ 0x06, 0x41, 0x40, 0xf0, 0x0f, 0x3c, 0x3d, 0xef, 0x0e, 0xee, 0xbe, 0xbf, 0x11, 0xf2, 0x0e, 0x3e,
+ 0x3c, 0x3f, 0x3e, 0x3e, 0xf0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc3, 0xc0, 0xc1, 0xc1, 0x0b, 0x3f, 0xf5,
+ 0xc0, 0xc0, 0x08, 0xf7, 0xc2, 0x0c, 0xf6, 0x0d, 0xf9, 0x0a, 0x3f, 0xf9, 0xc0, 0x0b, 0xf6, 0x06,
+ 0xf8, 0x06, 0xf9, 0xc3, 0x08, 0x40, 0xfa, 0xc2, 0x09, 0x3e, 0xf7, 0x05, 0xf6, 0x04, 0xf7, 0x05,
+ 0xf8, 0xc1, 0xc3, 0xc1, 0x0a, 0x3e, 0x41, 0xf6, 0x07, 0xf7, 0x08, 0xf6, 0x07, 0x3e, 0x3c, 0x3f,
+ 0xf4, 0xbf, 0xbe, 0x06, 0xf8, 0x07, 0xf8, 0x05, 0xfb, 0xc0, 0xc1, 0x02, 0x40, 0x40, 0x3e, 0xfb,
+ 0x02, 0x3e, 0x3e, 0xf6, 0x01, 0xf9, 0x00, 0x3f, 0xf8, 0xbd, 0x02, 0xfc, 0xc1, 0xc0, 0x03, 0xfe,
+ 0xc5, 0xc4, 0xc2, 0xc5, 0xc2, 0x02, 0x41, 0x40, 0x40, 0x41, 0x40, 0xff, 0xff, 0x40, 0xfd, 0xfd,
+ 0xfc, 0xfd, 0x3e, 0xfc, 0xbe, 0xbf, 0xfe, 0xfc, 0xc1, 0xc0, 0xc1, 0xc1, 0x00, 0x01, 0x01, 0x02,
+ 0xc0, 0x00, 0x02, 0xc3, 0xc5, 0x02, 0x07, 0xc4, 0xc6, 0xc4, 0x02, 0x06, 0xc3, 0x01, 0x43, 0x43,
+ 0x42, 0x05, 0xfc, 0x04, 0xfd, 0x41, 0x3e, 0x05, 0xbf, 0xfb, 0x41, 0x3e, 0x3e, 0x3f, 0x3f, 0x40,
+ 0x02, 0xf9, 0x3e, 0x03, 0xbf, 0xbe, 0xf6, 0x3f, 0x05, 0xf6, 0x3c, 0x05, 0xbd, 0xbf, 0xbe, 0xf2,
+ 0x3f, 0x3c, 0x3d, 0x3e, 0x08, 0xee, 0x3d, 0x3b, 0x07, 0xbd, 0xf3, 0x0e, 0xc0, 0xc0, 0xf2, 0x0f,
+ 0xf2, 0x40, 0x0d, 0xef, 0x3f, 0x3e, 0x0a, 0xbf, 0xf0, 0x0e, 0xf1, 0x40, 0x0a, 0xef, 0x0e, 0xc1,
+ 0xc0, 0xc1, 0xc2, 0xc0, 0xee, 0x13, 0xc1, 0xee, 0x43, 0x10, 0xea, 0x15, 0xc0, 0xc0, 0xc0, 0xc3,
+ 0xc0, 0xec, 0x15, 0xc2, 0xf0, 0x16, 0xc2, 0xef, 0x1b, 0xc2, 0xf1, 0x44, 0x42, 0x19, 0xc2, 0xc3,
+ 0xed, 0x17, 0xe7, 0x21, 0xe4, 0x40, 0x3f, 0x20, 0xc0, 0xe2, 0x40, 0x3e, 0x1d, 0xc1, 0xc2, 0xc0,
+ 0xe2, 0x20, 0xe2, 0x3f, 0x3f, 0x3e, 0x1c, 0xdf, 0x3c, 0x3f, 0x1e, 0xbf, 0xc1, 0xbe, 0xdf, 0x3e,
+ 0x3d, 0x1f, 0xdc, 0x3c, 0x3b, 0x3d, 0x3a, 0x3d, 0x1f, 0xdb, 0x1e, 0xda, 0x20, 0xda, 0x21, 0xc1,
+ 0xc0, 0xc1, 0xdc, 0x3e, 0x23, 0xda, 0x3e, 0x20, 0xbf, 0xbd, 0xdb, 0x23, 0xbf, 0xdf, 0x26, 0xdc,
+ 0x26, 0xdc, 0x2e, 0xc2, 0xc3, 0xc3, 0xd4, 0x40, 0x2f, 0xc2, 0xd2, 0x3f, 0x3f, 0x3f, 0x2e, 0xd1,
+ 0x2d, 0xd3, 0x2c, 0xc3, 0xc3, 0xc3, 0xc2, 0xc3, 0xc2, 0xd3, 0x3e, 0x31, 0xd0, 0x2d, 0xc3, 0xc0,
+ 0xc1, 0xd1, 0x2f, 0xd3, 0x3f, 0x3f, 0x31, 0xc1, 0xcf, 0x31, 0xd2, 0x3e, 0x3f, 0x3c, 0x41, 0x3e,
+ 0x3e, 0x3f, 0x31, 0xd1, 0x30, 0xc0, 0xcf, 0x33, 0xd1, 0x3d, 0x41, 0x32, 0xc0, 0xc3, 0xce, 0x3e,
+ 0x32, 0xc3, 0xc3, 0xc2, 0xc0, 0xc3, 0xca, 0x3c, 0x37, 0xc5, 0xc3, 0xca, 0x34, 0xcf, 0x35, 0xc6,
+ 0xca, 0x3c, 0x39, 0xce, 0x3c, 0x37, 0xc2, 0xc8, 0x3c, 0x36, 0xcc, 0x30, 0xcb, 0x33, 0xc6, 0xc3,
+ 0xc8, 0x34, 0xcc, 0x3d, 0x37, 0xc5, 0xc9, 0x38, 0xc6, 0xca, 0x3b, 0x40, 0x40, 0x40, 0x3a, 0xc5,
+ 0xc8, 0x3f, 0x3e, 0x3b, 0xce, 0x3a, 0x3e, 0x38, 0xca, 0x39, 0x3c, 0x3c, 0x36, 0xcb, 0x36, 0x3b,
+ 0x37, 0xc8, 0x35, 0x3a, 0x36, 0xc8, 0x2f, 0xc4, 0xc5, 0x36, 0x38, 0xc7, 0xc0, 0xc7, 0x33, 0xc8,
+ 0xc3, 0xc5, 0x39, 0x3b, 0xc9, 0xc7, 0x34, 0xd1, 0x34, 0xcf, 0x35, 0xca, 0xc9, 0x36, 0xd0, 0x36,
+ 0xcc, 0xc7, 0x39, 0x41, 0x42, 0x42, 0x3e, 0xca, 0xc1, 0xc3, 0xc3, 0xc2, 0xc3, 0xc2, 0xc4, 0xc6,
+ 0x34, 0xd1, 0x35, 0xcf, 0x37, 0x3c, 0xce, 0x35, 0x3b, 0xcc, 0x30, 0xca, 0x33, 0x39, 0xc9, 0xbf,
+ 0xbf, 0xbf, 0xc3, 0x34, 0x3c, 0x3f, 0x3a, 0xcc, 0x2f, 0xcd, 0x32, 0x3c, 0x3a, 0xc8, 0xc2, 0x34,
+ 0x3a, 0xcb, 0xc0, 0xc0, 0x35, 0x3c, 0xce, 0x32, 0x3c, 0x3c, 0x3d, 0x3a, 0xcd, 0x2e, 0x3b, 0x39,
+ 0xc9, 0xbc, 0xbf, 0x30, 0xcc, 0xc0, 0xc3, 0x33, 0xce, 0xc5, 0xc2, 0xc4, 0x36, 0x3e, 0xd1, 0xc3,
+ 0xc2, 0xc3, 0xc5, 0x33, 0xd0, 0xc5, 0xc4, 0x35, 0x40, 0x40, 0x3e, 0x41, 0x3e, 0xd2, 0x30, 0x3f,
+ 0x3e, 0x3d, 0x21, 0xc8, 0x0d, 0x23, 0xec, 0xb1, 0x1a, 0xf0, 0xf3, 0x10, 0xc6, 0xbd, 0x5b, 0x0d,
+ 0xff, 0xe2, 0xe6, 0xd4, 0x72, 0xe2, 0xda, 0x0c, 0xf8, 0x1b, 0x04, 0x0b, 0xf7, 0xb4, 0xf0, 0x44,
+ 0xcd, 0xaf, 0x12, 0x1f, 0x11, 0xa1, 0x29, 0xb8, 0x1a, 0xdb, 0x09, 0x69, 0xf7, 0x20, 0xc4, 0x1c,
+ 0xc0, 0xc8, 0x1d, 0x45, 0xd9, 0x30, 0xd8, 0x2e, 0xd8, 0x2e, 0xd6, 0x2c, 0xd5, 0x2d, 0xd4, 0x2b,
+ 0xd5, 0x2b, 0xd1, 0x31, 0xd0, 0x32, 0xd2, 0x30, 0xd0, 0x31, 0xd0, 0x31, 0xce, 0x2f, 0xd3, 0x2e,
+ 0xce, 0x2f, 0xd1, 0x31, 0x3c, 0xd1, 0x2d, 0xd0, 0xc0, 0x2e, 0x3c, 0x3d, 0xd1, 0xc3, 0x2c, 0xd2,
+ 0xc3, 0xc5, 0x31, 0x40, 0x40, 0xd5, 0x2c, 0x3a, 0x3d, 0xd0, 0x2b, 0xd3, 0xc0, 0xc0, 0x2d, 0xd4,
+ 0x2c, 0x3f, 0x3e, 0x3c, 0x3f, 0x3c, 0xd2, 0xc1, 0xc2, 0xc0, 0xc1, 0xc1, 0xc1, 0xc1, 0xc1, 0x2a,
+};
+
+
+int
+ao_real_packet(void)
+{
+       uint8_t decode[64];
+       int     ok;
+
+       ok = ao_fec_decode(real_packet, 576, decode, 34, NULL);
+
+       if (ok && decode[33] == AO_FEC_DECODE_CRC_OK) {
+               printf ("match\n");
+
+               ao_fec_dump_bytes(decode, 34, "Decode");
+       } else {
+               printf ("actual packet crc error\n");
+               ok = 0;
+       }
+       return ok;
+}
+
+#define EXPECT_DECODE_FAIL     0
+#define EXPECT_CRC_MISMATCH    6386
+#define EXPECT_DATA_MISMATCH   0
+#define NOISE_AMOUNT           0x50
+
+int
+main(int argc, char **argv)
+{
+       int             trial;
+
+       uint8_t         original[120];
+       uint8_t         original_len;
+
+       uint8_t         encode[ENCODE_LEN(sizeof(original))];
+       int             encode_len;
+
+       uint8_t         transmit[EXPAND_LEN(sizeof(original))];
+       int             transmit_len;
+
+       uint8_t         receive[EXPAND_LEN(sizeof(original))];
+       int             receive_len;
+
+       uint8_t         decode[DECODE_LEN(sizeof(original))];
+       int             decode_ok;
+
+       int             errors = 0;
+       int             decode_fail = 0;
+       int             crc_mismatch = 0;
+       int             data_mismatch = 0;
+
+       if (!ao_real_packet())
+               errors++;
+
+       srandom(0);
+       for (trial = 0; trial < 100000; trial++) {
+
+               /* Compute some random data */
+               original_len = ao_random_data(original, sizeof(original));
+
+               /* Encode it */
+               encode_len = ao_fec_encode(original, original_len, encode);
+
+               /* Expand from 1-bit-per-symbol to 1-byte-per-symbol */
+               transmit_len = ao_expand(encode, encode_len, transmit);
+
+               /* Add gaussian noise to the signal */
+               (void) ao_fuzz(transmit, transmit_len, receive, NOISE_AMOUNT);
+               receive_len = transmit_len;
+               
+               /* Decode it */
+               decode_ok = ao_fec_decode(receive, receive_len, decode, original_len + 2, NULL);
+
+               /* Check to see if we received the right data */
+
+               if (!decode_ok)
+                       decode_fail++;
+               else if (decode[original_len +1] != AO_FEC_DECODE_CRC_OK)
+                       crc_mismatch++;
+               else if (memcmp(original, decode, original_len) != 0)
+                       data_mismatch++;
+       }
+
+
+       printf ("%d packets coded\n", trial);
+       printf ("decode_fail %d crc_mismatch %d data_mismatch %d\n",
+               decode_fail, crc_mismatch, data_mismatch);
+       if (decode_fail != EXPECT_DECODE_FAIL) {
+               printf ("expected %d decode failures\n", EXPECT_DECODE_FAIL);
+               errors++;
+       }
+       if (crc_mismatch != EXPECT_CRC_MISMATCH) {
+               printf ("expected %d crc mismatch\n", EXPECT_CRC_MISMATCH);
+               errors++;
+       }
+       if (data_mismatch != EXPECT_DATA_MISMATCH) {
+               printf ("expected %d data mismatch\n", EXPECT_DATA_MISMATCH);
+               errors++;
+       }
+       return errors;
+}
+
+
diff --git a/src/test/ao_flight_test.c b/src/test/ao_flight_test.c
new file mode 100644 (file)
index 0000000..b9e291c
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define _GNU_SOURCE
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+#include <math.h>
+
+#define AO_HERTZ       100
+
+#define HAS_ADC 1
+#define AO_DATA_RING   64
+#define ao_data_ring_next(n)   (((n) + 1) & (AO_DATA_RING - 1))
+#define ao_data_ring_prev(n)   (((n) - 1) & (AO_DATA_RING - 1))
+
+#define AO_M_TO_HEIGHT(m)      ((int16_t) (m))
+#define AO_MS_TO_SPEED(ms)     ((int16_t) ((ms) * 16))
+#define AO_MSS_TO_ACCEL(mss)   ((int16_t) ((mss) * 16))
+
+/*
+ * One set of samples read from the A/D converter
+ */
+struct ao_adc {
+       int16_t         accel;          /* accelerometer */
+       int16_t         pres;           /* pressure sensor */
+       int16_t         pres_real;      /* unclipped */
+       int16_t         temp;           /* temperature sensor */
+       int16_t         v_batt;         /* battery voltage */
+       int16_t         sense_d;        /* drogue continuity sense */
+       int16_t         sense_m;        /* main continuity sense */
+};
+
+#define __pdata
+#define __data
+#define __xdata
+#define __code
+#define __reentrant
+
+#define HAS_FLIGHT 1
+#define HAS_IGNITE 1
+#define HAS_USB 1
+#define HAS_GPS 1
+#ifndef HAS_ACCEL
+#define HAS_ACCEL 1
+#define HAS_ACCEL_REF 0
+#endif
+
+#include <ao_data.h>
+
+#define to_fix16(x) ((int16_t) ((x) * 65536.0 + 0.5))
+#define to_fix32(x) ((int32_t) ((x) * 65536.0 + 0.5))
+#define from_fix(x)    ((x) >> 16)
+
+/*
+ * Above this height, the baro sensor doesn't work
+ */
+#define AO_MAX_BARO_HEIGHT     12000
+#define AO_BARO_SATURATE       13000
+#define AO_MIN_BARO_VALUE      ao_altitude_to_pres(AO_BARO_SATURATE)
+
+/*
+ * Above this speed, baro measurements are unreliable
+ */
+#define AO_MAX_BARO_SPEED      200
+
+#define ACCEL_NOSE_UP  (ao_accel_2g >> 2)
+
+enum ao_flight_state {
+       ao_flight_startup = 0,
+       ao_flight_idle = 1,
+       ao_flight_pad = 2,
+       ao_flight_boost = 3,
+       ao_flight_fast = 4,
+       ao_flight_coast = 5,
+       ao_flight_drogue = 6,
+       ao_flight_main = 7,
+       ao_flight_landed = 8,
+       ao_flight_invalid = 9
+};
+
+extern enum ao_flight_state ao_flight_state;
+
+#define FALSE 0
+#define TRUE 1
+
+volatile struct ao_data ao_data_ring[AO_DATA_RING];
+volatile uint8_t ao_data_head;
+int    ao_summary = 0;
+
+#define ao_led_on(l)
+#define ao_led_off(l)
+#define ao_timer_set_adc_interval(i)
+#define ao_wakeup(wchan) ao_dump_state()
+#define ao_cmd_register(c)
+#define ao_usb_disable()
+#define ao_telemetry_set_interval(x)
+#define ao_rdf_set(rdf)
+#define ao_packet_slave_start()
+#define ao_packet_slave_stop()
+
+enum ao_igniter {
+       ao_igniter_drogue = 0,
+       ao_igniter_main = 1
+};
+
+struct ao_data ao_data_static;
+
+int    drogue_height;
+double drogue_time;
+int    main_height;
+double main_time;
+
+int    tick_offset;
+
+static int32_t ao_k_height;
+
+void
+ao_ignite(enum ao_igniter igniter)
+{
+       double time = (double) (ao_data_static.tick + tick_offset) / 100;
+
+       if (igniter == ao_igniter_drogue) {
+               drogue_time = time;
+               drogue_height = ao_k_height >> 16;
+       } else {
+               main_time = time;
+               main_height = ao_k_height >> 16;
+       }
+}
+
+struct ao_task {
+       int dummy;
+};
+
+#define ao_add_task(t,f,n) ((void) (t))
+
+#define ao_log_start()
+#define ao_log_stop()
+
+#define AO_MS_TO_TICKS(ms)     ((ms) / 10)
+#define AO_SEC_TO_TICKS(s)     ((s) * 100)
+
+#define AO_FLIGHT_TEST
+
+int    ao_flight_debug;
+
+FILE *emulator_in;
+char *emulator_app;
+char *emulator_name;
+char *emulator_info;
+double emulator_error_max = 4;
+double emulator_height_error_max = 20; /* noise in the baro sensor */
+
+void
+ao_dump_state(void);
+
+void
+ao_sleep(void *wchan);
+
+const char const * const ao_state_names[] = {
+       "startup", "idle", "pad", "boost", "fast",
+       "coast", "drogue", "main", "landed", "invalid"
+};
+
+struct ao_cmds {
+       void            (*func)(void);
+       const char      *help;
+};
+
+#define ao_xmemcpy(d,s,c) memcpy(d,s,c)
+#define ao_xmemset(d,v,c) memset(d,v,c)
+#define ao_xmemcmp(d,s,c) memcmp(d,s,c)
+
+#define AO_NEED_ALTITUDE_TO_PRES 1
+#include "ao_convert.c"
+
+struct ao_config {
+       uint16_t        main_deploy;
+       int16_t         accel_plus_g;
+       int16_t         accel_minus_g;
+       uint8_t         pad_orientation;
+       uint16_t        apogee_lockout;
+};
+
+#define AO_PAD_ORIENTATION_ANTENNA_UP  0
+#define AO_PAD_ORIENTATION_ANTENNA_DOWN        1
+
+#define ao_config_get()
+
+struct ao_config ao_config;
+
+#define DATA_TO_XDATA(x) (x)
+
+
+#define GRAVITY 9.80665
+extern int16_t ao_ground_accel, ao_flight_accel;
+extern int16_t ao_accel_2g;
+
+typedef int16_t        accel_t;
+
+extern uint16_t        ao_sample_tick;
+
+extern int16_t ao_sample_height;
+extern accel_t ao_sample_accel;
+extern int32_t ao_accel_scale;
+extern int16_t ao_ground_height;
+extern int16_t ao_sample_alt;
+
+int ao_sample_prev_tick;
+uint16_t       prev_tick;
+
+#include "ao_kalman.c"
+#include "ao_sample.c"
+#include "ao_flight.c"
+
+#define to_double(f)   ((f) / 65536.0)
+
+static int     ao_records_read = 0;
+static int     ao_eof_read = 0;
+static int     ao_flight_ground_accel;
+static int     ao_flight_started = 0;
+static int     ao_test_max_height;
+static double  ao_test_max_height_time;
+static int     ao_test_main_height;
+static double  ao_test_main_height_time;
+static double  ao_test_landed_time;
+static double  ao_test_landed_height;
+static double  ao_test_landed_time;
+static int     landed_set;
+static double  landed_time;
+static double  landed_height;
+
+void
+ao_test_exit(void)
+{
+       double  drogue_error;
+       double  main_error;
+       double  landed_error;
+       double  landed_time_error;
+
+       if (!ao_test_main_height_time) {
+               ao_test_main_height_time = ao_test_max_height_time;
+               ao_test_main_height = ao_test_max_height;
+       }
+       drogue_error = fabs(ao_test_max_height_time - drogue_time);
+       main_error = fabs(ao_test_main_height_time - main_time);
+       landed_error = fabs(ao_test_landed_height - landed_height);
+       landed_time_error = ao_test_landed_time - landed_time;
+       if (drogue_error > emulator_error_max || main_error > emulator_error_max) {
+               printf ("%s %s\n",
+                       emulator_app, emulator_name);
+               if (emulator_info)
+                       printf ("\t%s\n", emulator_info);
+               printf ("\tApogee error %g\n", drogue_error);
+               printf ("\tMain error %g\n", main_error);
+               printf ("\tLanded height error %g\n", landed_error);
+               printf ("\tLanded time error %g\n", landed_time_error);
+               printf ("\tActual: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
+                       ao_test_max_height, ao_test_max_height_time,
+                       ao_test_main_height, ao_test_main_height_time,
+                       ao_test_landed_height, ao_test_landed_time);
+               printf ("\tComputed: apogee: %d at %7.2f main: %d at %7.2f landed %7.2f at %7.2f\n",
+                       drogue_height, drogue_time, main_height, main_time,
+                       landed_height, landed_time);
+               exit (1);
+       }
+       exit(0);
+}
+
+void
+ao_insert(void)
+{
+       double  time;
+
+       ao_data_ring[ao_data_head] = ao_data_static;
+       ao_data_head = ao_data_ring_next(ao_data_head);
+       if (ao_flight_state != ao_flight_startup) {
+               double  height = ao_pres_to_altitude(ao_data_static.adc.pres_real) - ao_ground_height;
+               double  accel = ((ao_flight_ground_accel - ao_data_static.adc.accel) * GRAVITY * 2.0) /
+                       (ao_config.accel_minus_g - ao_config.accel_plus_g);
+
+               if (!tick_offset)
+                       tick_offset = -ao_data_static.tick;
+               if ((prev_tick - ao_data_static.tick) > 0x400)
+                       tick_offset += 65536;
+               prev_tick = ao_data_static.tick;
+               time = (double) (ao_data_static.tick + tick_offset) / 100;
+
+               if (ao_test_max_height < height) {
+                       ao_test_max_height = height;
+                       ao_test_max_height_time = time;
+                       ao_test_landed_height = height;
+                       ao_test_landed_time = time;
+               }
+               if (height > ao_config.main_deploy) {
+                       ao_test_main_height_time = time;
+                       ao_test_main_height = height;
+               }
+
+               if (ao_test_landed_height > height) {
+                       ao_test_landed_height = height;
+                       ao_test_landed_time = time;
+               }
+
+               if (ao_flight_state == ao_flight_landed && !landed_set) {
+                       landed_set = 1;
+                       landed_time = time;
+                       landed_height = height;
+               }
+
+               if (!ao_summary) {
+                       printf("%7.2f height %8.2f accel %8.3f state %-8.8s k_height %8.2f k_speed %8.3f k_accel %8.3f avg_height %5d drogue %4d main %4d error %5d\n",
+                              time,
+                              height,
+                              accel,
+                              ao_state_names[ao_flight_state],
+                              ao_k_height / 65536.0,
+                              ao_k_speed / 65536.0 / 16.0,
+                              ao_k_accel / 65536.0 / 16.0,
+                              ao_avg_height,
+                              drogue_height,
+                              main_height,
+                              ao_error_h_sq_avg);
+                       
+//                     if (ao_flight_state == ao_flight_landed)
+//                             ao_test_exit();
+               }
+       }
+}
+
+#define AO_MAX_CALLSIGN                        8
+#define AO_MAX_VERSION                 8
+#define AO_MAX_TELEMETRY               128
+
+struct ao_telemetry_generic {
+       uint16_t        serial;         /* 0 */
+       uint16_t        tick;           /* 2 */
+       uint8_t         type;           /* 4 */
+       uint8_t         payload[27];    /* 5 */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_SENSOR_TELEMETRUM 0x01
+#define AO_TELEMETRY_SENSOR_TELEMINI   0x02
+#define AO_TELEMETRY_SENSOR_TELENANO   0x03
+
+struct ao_telemetry_sensor {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         state;          /*  5 flight state */
+       int16_t         accel;          /*  6 accelerometer (TM only) */
+       int16_t         pres;           /*  8 pressure sensor */
+       int16_t         temp;           /* 10 temperature sensor */
+       int16_t         v_batt;         /* 12 battery voltage */
+       int16_t         sense_d;        /* 14 drogue continuity sense (TM/Tm) */
+       int16_t         sense_m;        /* 16 main continuity sense (TM/Tm) */
+
+       int16_t         acceleration;   /* 18 m/s² * 16 */
+       int16_t         speed;          /* 20 m/s * 16 */
+       int16_t         height;         /* 22 m */
+
+       int16_t         ground_pres;    /* 24 average pres on pad */
+       int16_t         ground_accel;   /* 26 average accel on pad */
+       int16_t         accel_plus_g;   /* 28 accel calibration at +1g */
+       int16_t         accel_minus_g;  /* 30 accel calibration at -1g */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_CONFIGURATION     0x04
+
+struct ao_telemetry_configuration {
+       uint16_t        serial;                         /*  0 */
+       uint16_t        tick;                           /*  2 */
+       uint8_t         type;                           /*  4 */
+
+       uint8_t         device;                         /*  5 device type */
+       uint16_t        flight;                         /*  6 flight number */
+       uint8_t         config_major;                   /*  8 Config major version */
+       uint8_t         config_minor;                   /*  9 Config minor version */
+       uint16_t        apogee_delay;                   /* 10 Apogee deploy delay in seconds */
+       uint16_t        main_deploy;                    /* 12 Main deploy alt in meters */
+       uint16_t        flight_log_max;                 /* 14 Maximum flight log size in kB */
+       char            callsign[AO_MAX_CALLSIGN];      /* 16 Radio operator identity */
+       char            version[AO_MAX_VERSION];        /* 24 Software version */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_LOCATION          0x05
+
+#define AO_GPS_MODE_NOT_VALID          'N'
+#define AO_GPS_MODE_AUTONOMOUS         'A'
+#define AO_GPS_MODE_DIFFERENTIAL       'D'
+#define AO_GPS_MODE_ESTIMATED          'E'
+#define AO_GPS_MODE_MANUAL             'M'
+#define AO_GPS_MODE_SIMULATED          'S'
+
+struct ao_telemetry_location {
+       uint16_t        serial;         /*  0 */
+       uint16_t        tick;           /*  2 */
+       uint8_t         type;           /*  4 */
+
+       uint8_t         flags;          /*  5 Number of sats and other flags */
+       int16_t         altitude;       /*  6 GPS reported altitude (m) */
+       int32_t         latitude;       /*  8 latitude (degrees * 10⁷) */
+       int32_t         longitude;      /* 12 longitude (degrees * 10⁷) */
+       uint8_t         year;           /* 16 (- 2000) */
+       uint8_t         month;          /* 17 (1-12) */
+       uint8_t         day;            /* 18 (1-31) */
+       uint8_t         hour;           /* 19 (0-23) */
+       uint8_t         minute;         /* 20 (0-59) */
+       uint8_t         second;         /* 21 (0-59) */
+       uint8_t         pdop;           /* 22 (m * 5) */
+       uint8_t         hdop;           /* 23 (m * 5) */
+       uint8_t         vdop;           /* 24 (m * 5) */
+       uint8_t         mode;           /* 25 */
+       uint16_t        ground_speed;   /* 26 cm/s */
+       int16_t         climb_rate;     /* 28 cm/s */
+       uint8_t         course;         /* 30 degrees / 2 */
+       uint8_t         unused[1];      /* 31 */
+       /* 32 */
+};
+
+#define AO_TELEMETRY_SATELLITE         0x06
+
+struct ao_telemetry_satellite_info {
+       uint8_t         svid;
+       uint8_t         c_n_1;
+};
+
+struct ao_telemetry_satellite {
+       uint16_t                                serial;         /*  0 */
+       uint16_t                                tick;           /*  2 */
+       uint8_t                                 type;           /*  4 */
+       uint8_t                                 channels;       /*  5 number of reported sats */
+
+       struct ao_telemetry_satellite_info      sats[12];       /* 6 */
+       uint8_t                                 unused[2];      /* 30 */
+       /* 32 */
+};
+
+union ao_telemetry_all {
+       struct ao_telemetry_generic             generic;
+       struct ao_telemetry_sensor              sensor;
+       struct ao_telemetry_configuration       configuration;
+       struct ao_telemetry_location            location;
+       struct ao_telemetry_satellite           satellite;
+};
+
+uint16_t
+uint16(uint8_t *bytes, int off)
+{
+       off++;
+       return (uint16_t) bytes[off] | (((uint16_t) bytes[off+1]) << 8);
+}
+
+int16_t
+int16(uint8_t *bytes, int off)
+{
+       return (int16_t) uint16(bytes, off);
+}
+
+void
+ao_sleep(void *wchan)
+{
+       if (wchan == &ao_data_head) {
+               char            type = 0;
+               uint16_t        tick = 0;
+               uint16_t        a = 0, b = 0;
+               uint8_t         bytes[1024];
+               union ao_telemetry_all  telem;
+               char            line[1024];
+               char            *saveptr;
+               char            *l;
+               char            *words[64];
+               int             nword;
+
+               for (;;) {
+                       if (ao_records_read > 2 && ao_flight_state == ao_flight_startup)
+                       {
+                               ao_data_static.adc.accel = ao_flight_ground_accel;
+                               ao_insert();
+                               return;
+                       }
+
+                       if (!fgets(line, sizeof (line), emulator_in)) {
+                               if (++ao_eof_read >= 1000) {
+                                       if (!ao_summary)
+                                               printf ("no more data, exiting simulation\n");
+                                       ao_test_exit();
+                               }
+                               ao_data_static.tick += 10;
+                               ao_insert();
+                               return;
+                       }
+                       l = line;
+                       for (nword = 0; nword < 64; nword++) {
+                               words[nword] = strtok_r(l, " \t\n", &saveptr);
+                               l = NULL;
+                               if (words[nword] == NULL)
+                                       break;
+                       }
+                       if (nword == 4) {
+                               type = words[0][0];
+                               tick = strtoul(words[1], NULL, 16);
+                               a = strtoul(words[2], NULL, 16);
+                               b = strtoul(words[3], NULL, 16);
+                               if (type == 'P')
+                                       type = 'A';
+                       } else if (nword >= 6 && strcmp(words[0], "Accel") == 0) {
+                               ao_config.accel_plus_g = atoi(words[3]);
+                               ao_config.accel_minus_g = atoi(words[5]);
+                       } else if (nword >= 4 && strcmp(words[0], "Main") == 0) {
+                               ao_config.main_deploy = atoi(words[2]);
+                       } else if (nword >= 3 && strcmp(words[0], "Apogee") == 0 &&
+                                  strcmp(words[1], "lockout:") == 0) {
+                               ao_config.apogee_lockout = atoi(words[2]);
+                       } else if (nword >= 36 && strcmp(words[0], "CALL") == 0) {
+                               tick = atoi(words[10]);
+                               if (!ao_flight_started) {
+                                       type = 'F';
+                                       a = atoi(words[26]);
+                                       ao_flight_started = 1;
+                               } else {
+                                       type = 'A';
+                                       a = atoi(words[12]);
+                                       b = atoi(words[14]);
+                               }
+                       } else if (nword == 3 && strcmp(words[0], "BARO") == 0) {
+                               tick = strtol(words[1], NULL, 16);
+                               a = 16384 - 328;
+                               b = strtol(words[2], NULL, 10);
+                               type = 'A';
+                               if (!ao_flight_started) {
+                                       ao_flight_ground_accel = 16384 - 328;
+                                       ao_config.accel_plus_g = 16384 - 328;
+                                       ao_config.accel_minus_g = 16384 + 328;
+                                       ao_flight_started = 1;
+                               }
+                       } else if (nword == 2 && strcmp(words[0], "TELEM") == 0) {
+                               __xdata char    *hex = words[1];
+                               char    elt[3];
+                               int     i, len;
+                               uint8_t sum;
+
+                               len = strlen(hex);
+                               if (len > sizeof (bytes) * 2) {
+                                       len = sizeof (bytes)*2;
+                                       hex[len] = '\0';
+                               }
+                               for (i = 0; i < len; i += 2) {
+                                       elt[0] = hex[i];
+                                       elt[1] = hex[i+1];
+                                       elt[2] = '\0';
+                                       bytes[i/2] = (uint8_t) strtol(elt, NULL, 16);
+                               }
+                               len = i/2;
+                               if (bytes[0] != len - 2) {
+                                       printf ("bad length %d != %d\n", bytes[0], len - 2);
+                                       continue;
+                               }
+                               sum = 0x5a;
+                               for (i = 1; i < len-1; i++)
+                                       sum += bytes[i];
+                               if (sum != bytes[len-1]) {
+                                       printf ("bad checksum\n");
+                                       continue;
+                               }
+                               if ((bytes[len-2] & 0x80) == 0) {
+                                       continue;
+                               }
+                               if (len == 36) {
+                                       ao_xmemcpy(&telem, bytes + 1, 32);
+                                       tick = telem.generic.tick;
+                                       switch (telem.generic.type) {
+                                       case AO_TELEMETRY_SENSOR_TELEMETRUM:
+                                       case AO_TELEMETRY_SENSOR_TELEMINI:
+                                       case AO_TELEMETRY_SENSOR_TELENANO:
+                                               if (!ao_flight_started) {
+                                                       ao_flight_ground_accel = telem.sensor.ground_accel;
+                                                       ao_config.accel_plus_g = telem.sensor.accel_plus_g;
+                                                       ao_config.accel_minus_g = telem.sensor.accel_minus_g;
+                                                       ao_flight_started = 1;
+                                               }
+                                               type = 'A';
+                                               a = telem.sensor.accel;
+                                               b = telem.sensor.pres;
+                                               break;
+                                       }
+                               } else if (len == 99) {
+                                       ao_flight_started = 1;
+                                       tick = uint16(bytes, 21);
+                                       ao_flight_ground_accel = int16(bytes, 7);
+                                       ao_config.accel_plus_g = int16(bytes, 17);
+                                       ao_config.accel_minus_g = int16(bytes, 19);
+                                       type = 'A';
+                                       a = int16(bytes, 23);
+                                       b = int16(bytes, 25);
+                               } else if (len == 98) {
+                                       ao_flight_started = 1;
+                                       tick = uint16(bytes, 20);
+                                       ao_flight_ground_accel = int16(bytes, 6);
+                                       ao_config.accel_plus_g = int16(bytes, 16);
+                                       ao_config.accel_minus_g = int16(bytes, 18);
+                                       type = 'A';
+                                       a = int16(bytes, 22);
+                                       b = int16(bytes, 24);
+                               } else {
+                                       printf("unknown len %d\n", len);
+                                       continue;
+                               }
+                       }
+                       if (type != 'F' && !ao_flight_started)
+                               continue;
+
+                       switch (type) {
+                       case 'F':
+                               ao_flight_ground_accel = a;
+                               if (ao_config.accel_plus_g == 0) {
+                                       ao_config.accel_plus_g = a;
+                                       ao_config.accel_minus_g = a + 530;
+                               }
+                               if (ao_config.main_deploy == 0)
+                                       ao_config.main_deploy = 250;
+                               ao_flight_started = 1;
+                               break;
+                       case 'S':
+                               break;
+                       case 'A':
+                               ao_data_static.tick = tick;
+                               ao_data_static.adc.accel = a;
+                               ao_data_static.adc.pres_real = b;
+                               if (b < AO_MIN_BARO_VALUE)
+                                       b = AO_MIN_BARO_VALUE;
+                               ao_data_static.adc.pres = b;
+                               ao_records_read++;
+                               ao_insert();
+                               return;
+                       case 'T':
+                               ao_data_static.tick = tick;
+                               ao_data_static.adc.temp = a;
+                               ao_data_static.adc.v_batt = b;
+                               break;
+                       case 'D':
+                       case 'G':
+                       case 'N':
+                       case 'W':
+                       case 'H':
+                               break;
+                       }
+               }
+
+       }
+}
+#define COUNTS_PER_G 264.8
+
+void
+ao_dump_state(void)
+{
+}
+
+static const struct option options[] = {
+       { .name = "summary", .has_arg = 0, .val = 's' },
+       { .name = "debug", .has_arg = 0, .val = 'd' },
+       { .name = "info", .has_arg = 1, .val = 'i' },
+       { 0, 0, 0, 0},
+};
+
+void run_flight_fixed(char *name, FILE *f, int summary, char *info)
+{
+       emulator_name = name;
+       emulator_in = f;
+       emulator_info = info;
+       ao_summary = summary;
+       ao_flight_init();
+       ao_flight();
+}
+
+int
+main (int argc, char **argv)
+{
+       int     summary = 0;
+       int     c;
+       int     i;
+       char    *info = NULL;
+
+#if HAS_ACCEL
+       emulator_app="full";
+#else
+       emulator_app="baro";
+#endif
+       while ((c = getopt_long(argc, argv, "sdi:", options, NULL)) != -1) {
+               switch (c) {
+               case 's':
+                       summary = 1;
+                       break;
+               case 'd':
+                       ao_flight_debug = 1;
+                       break;
+               case 'i':
+                       info = optarg;
+                       break;
+               }
+       }
+
+       if (optind == argc)
+               run_flight_fixed("<stdin>", stdin, summary, info);
+       else
+               for (i = optind; i < argc; i++) {
+                       FILE    *f = fopen(argv[i], "r");
+                       if (!f) {
+                               perror(argv[i]);
+                               continue;
+                       }
+                       run_flight_fixed(argv[i], f, summary, info);
+                       fclose(f);
+               }
+       exit(0);
+}
diff --git a/src/test/ao_gps_test.c b/src/test/ao_gps_test.c
new file mode 100644 (file)
index 0000000..d75a12e
--- /dev/null
@@ -0,0 +1,513 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define AO_GPS_TEST
+#include "ao_host.h"
+#include <termios.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#define AO_GPS_NUM_SAT_MASK    (0xf << 0)
+#define AO_GPS_NUM_SAT_SHIFT   (0)
+
+#define AO_GPS_VALID           (1 << 4)
+#define AO_GPS_RUNNING         (1 << 5)
+#define AO_GPS_DATE_VALID      (1 << 6)
+#define AO_GPS_COURSE_VALID    (1 << 7)
+
+struct ao_gps_orig {
+       uint8_t                 year;
+       uint8_t                 month;
+       uint8_t                 day;
+       uint8_t                 hour;
+       uint8_t                 minute;
+       uint8_t                 second;
+       uint8_t                 flags;
+       int32_t                 latitude;       /* degrees * 10⁷ */
+       int32_t                 longitude;      /* degrees * 10⁷ */
+       int16_t                 altitude;       /* m */
+       uint16_t                ground_speed;   /* cm/s */
+       uint8_t                 course;         /* degrees / 2 */
+       uint8_t                 hdop;           /* * 5 */
+       int16_t                 climb_rate;     /* cm/s */
+       uint16_t                h_error;        /* m */
+       uint16_t                v_error;        /* m */
+};
+
+#define SIRF_SAT_STATE_ACQUIRED                        (1 << 0)
+#define SIRF_SAT_STATE_CARRIER_PHASE_VALID     (1 << 1)
+#define SIRF_SAT_BIT_SYNC_COMPLETE             (1 << 2)
+#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE                (1 << 3)
+#define SIRF_SAT_CARRIER_PULLIN_COMPLETE       (1 << 4)
+#define SIRF_SAT_CODE_LOCKED                   (1 << 5)
+#define SIRF_SAT_ACQUISITION_FAILED            (1 << 6)
+#define SIRF_SAT_EPHEMERIS_AVAILABLE           (1 << 7)
+
+struct ao_gps_sat_orig {
+       uint8_t         svid;
+       uint8_t         c_n_1;
+};
+
+#define AO_MAX_GPS_TRACKING    12
+
+struct ao_gps_tracking_orig {
+       uint8_t                 channels;
+       struct ao_gps_sat_orig  sats[AO_MAX_GPS_TRACKING];
+};
+
+#define ao_telemetry_location ao_gps_orig
+#define ao_telemetry_satellite ao_gps_tracking_orig
+#define ao_telemetry_satellite_info ao_gps_sat_orig
+
+void
+ao_mutex_get(uint8_t *mutex)
+{
+}
+
+void
+ao_mutex_put(uint8_t *mutex)
+{
+}
+
+static int
+ao_gps_fd;
+
+static void
+ao_dbg_char(char c)
+{
+       char    line[128];
+       line[0] = '\0';
+       if (c < ' ') {
+               if (c == '\n')
+                       sprintf (line, "\n");
+               else
+                       sprintf (line, "\\%02x", ((int) c) & 0xff);
+       } else {
+               sprintf (line, "%c", c);
+       }
+       write(1, line, strlen(line));
+}
+
+#define QUEUE_LEN      4096
+
+static char    input_queue[QUEUE_LEN];
+int            input_head, input_tail;
+
+#include <sys/time.h>
+
+int
+get_millis(void)
+{
+       struct timeval  tv;
+       gettimeofday(&tv, NULL);
+       return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static void
+check_sirf_message(char *from, uint8_t *msg, int len)
+{
+       uint16_t        encoded_len, encoded_cksum;
+       uint16_t        cksum;
+       uint8_t         id;
+       int             i;
+
+       if (msg[0] != 0xa0 || msg[1] != 0xa2) {
+               printf ("bad header\n");
+               return;
+       }
+       if (len < 7) {
+               printf("short\n");
+               return;
+       }
+       if (msg[len-1] != 0xb3 || msg[len-2] != 0xb0) {
+               printf ("bad trailer\n");
+               return;
+       }
+       encoded_len = (msg[2] << 8) | msg[3];
+       id = msg[4];
+/*     printf ("%9d: %3d\n", get_millis(), id); */
+       if (encoded_len != len - 8) {
+               if (id != 52)
+                       printf ("length mismatch (got %d, wanted %d)\n",
+                               len - 8, encoded_len);
+               return;
+       }
+       encoded_cksum = (msg[len - 4] << 8) | msg[len-3];
+       cksum = 0;
+       for (i = 4; i < len - 4; i++)
+               cksum = (cksum + msg[i]) & 0x7fff;
+       if (encoded_cksum != cksum) {
+               printf ("cksum mismatch (got %04x wanted %04x)\n",
+                       cksum, encoded_cksum);
+               return;
+       }
+       id = msg[4];
+       switch (id) {
+       case 41:{
+               int     off = 4;
+
+               uint8_t         id;
+               uint16_t        nav_valid;
+               uint16_t        nav_type;
+               uint16_t        week;
+               uint32_t        tow;
+               uint16_t        year;
+               uint8_t         month;
+               uint8_t         day;
+               uint8_t         hour;
+               uint8_t         minute;
+               uint16_t        second;
+               uint32_t        sat_list;
+               int32_t         lat;
+               int32_t         lon;
+               int32_t         alt_ell;
+               int32_t         alt_msl;
+               int8_t          datum;
+               uint16_t        sog;
+               uint16_t        cog;
+               int16_t         mag_var;
+               int16_t         climb_rate;
+               int16_t         heading_rate;
+               uint32_t        h_error;
+               uint32_t        v_error;
+               uint32_t        t_error;
+               uint16_t        h_v_error;
+
+#define get_u8(u)      u = (msg[off]); off+= 1
+#define get_u16(u)     u = (msg[off] << 8) | (msg[off + 1]); off+= 2
+#define get_u32(u)     u = (msg[off] << 24) | (msg[off + 1] << 16) | (msg[off+2] << 8) | (msg[off+3]); off+= 4
+
+               get_u8(id);
+               get_u16(nav_valid);
+               get_u16(nav_type);
+               get_u16(week);
+               get_u32(tow);
+               get_u16(year);
+               get_u8(month);
+               get_u8(day);
+               get_u8(hour);
+               get_u8(minute);
+               get_u16(second);
+               get_u32(sat_list);
+               get_u32(lat);
+               get_u32(lon);
+               get_u32(alt_ell);
+               get_u32(alt_msl);
+               get_u8(datum);
+               get_u16(sog);
+               get_u16(cog);
+               get_u16(mag_var);
+               get_u16(climb_rate);
+               get_u16(heading_rate);
+               get_u32(h_error);
+               get_u32(v_error);
+               get_u32(t_error);
+               get_u16(h_v_error);
+
+
+               (void) mag_var;
+               (void) id;
+               printf ("Geodetic Navigation Data (41):\n");
+               printf ("\tNav valid %04x\n", nav_valid);
+               printf ("\tNav type %04x\n", nav_type);
+               printf ("\tWeek %5d", week);
+               printf (" TOW %9d", tow);
+               printf (" %4d-%2d-%2d %02d:%02d:%07.4f\n",
+                       year, month, day,
+                       hour, minute, second / 1000.0);
+               printf ("\tsats: %08x\n", sat_list);
+               printf ("\tlat: %g", lat / 1.0e7);
+               printf (" lon: %g", lon / 1.0e7);
+               printf (" alt_ell: %g", alt_ell / 100.0);
+               printf (" alt_msll: %g", alt_msl / 100.0);
+               printf (" datum: %d\n", datum);
+               printf ("\tground speed: %g", sog / 100.0);
+               printf (" course: %g", cog / 100.0);
+               printf (" climb: %g", climb_rate / 100.0);
+               printf (" heading rate: %g\n", heading_rate / 100.0);
+               printf ("\th error: %g", h_error / 100.0);
+               printf (" v error: %g", v_error / 100.0);
+               printf (" t error: %g", t_error / 100.0);
+               printf (" h vel error: %g\n", h_v_error / 100.0);
+               break;
+       }
+       case 4: {
+               int off = 4;
+               uint8_t         id;
+               int16_t         gps_week;
+               uint32_t        gps_tow;
+               uint8_t         channels;
+               int             j, k;
+
+               get_u8(id);
+               get_u16(gps_week);
+               get_u32(gps_tow);
+               get_u8(channels);
+
+               (void) id;
+               printf ("Measured Tracker Data (4):\n");
+               printf ("GPS week: %d\n", gps_week);
+               printf ("GPS time of week: %d\n", gps_tow);
+               printf ("channels: %d\n", channels);
+               for (j = 0; j < 12; j++) {
+                       uint8_t svid, azimuth, elevation;
+                       uint16_t state;
+                       uint8_t c_n[10];
+                       get_u8(svid);
+                       get_u8(azimuth);
+                       get_u8(elevation);
+                       get_u16(state);
+                       for (k = 0; k < 10; k++) {
+                               get_u8(c_n[k]);
+                       }
+                       printf ("Sat %3d:", svid);
+                       printf (" aziumuth: %6.1f", azimuth * 1.5);
+                       printf (" elevation: %6.1f", elevation * 0.5);
+                       printf (" state: 0x%02x", state);
+                       printf (" c_n:");
+                       for (k = 0; k < 10; k++)
+                               printf(" %3d", c_n[k]);
+                       if (state & SIRF_SAT_STATE_ACQUIRED)
+                               printf(" acq,");
+                       if (state & SIRF_SAT_STATE_CARRIER_PHASE_VALID)
+                               printf(" car,");
+                       if (state & SIRF_SAT_BIT_SYNC_COMPLETE)
+                               printf(" bit,");
+                       if (state & SIRF_SAT_SUBFRAME_SYNC_COMPLETE)
+                               printf(" sub,");
+                       if (state & SIRF_SAT_CARRIER_PULLIN_COMPLETE)
+                               printf(" pullin,");
+                       if (state & SIRF_SAT_CODE_LOCKED)
+                               printf(" code,");
+                       if (state & SIRF_SAT_ACQUISITION_FAILED)
+                               printf(" fail,");
+                       if (state & SIRF_SAT_EPHEMERIS_AVAILABLE)
+                               printf(" ephem,");
+                       printf ("\n");
+               }
+               break;
+       }
+       default:
+               return;
+               printf ("%s %4d:", from, encoded_len);
+               for (i = 4; i < len - 4; i++) {
+                       if (((i - 4) & 0xf) == 0)
+                               printf("\n   ");
+                       printf (" %3d", msg[i]);
+               }
+               printf ("\n");
+       }
+}
+
+static uint8_t sirf_message[4096];
+static int     sirf_message_len;
+static uint8_t sirf_in_message[4096];
+static int     sirf_in_len;
+
+char
+ao_serial1_getchar(void)
+{
+       char    c;
+       uint8_t uc;
+
+       while (input_head == input_tail) {
+               for (;;) {
+                       input_tail = read(ao_gps_fd, input_queue, QUEUE_LEN);
+                       if (input_tail < 0) {
+                               if (errno == EINTR || errno == EAGAIN)
+                                       continue;
+                               perror ("getchar");
+                               exit (1);
+                       }
+                       input_head = 0;
+                       break;
+               }
+       }
+       c = input_queue[input_head];
+       input_head = (input_head + 1) % QUEUE_LEN;
+       uc = c;
+       if (sirf_in_len || uc == 0xa0) {
+               if (sirf_in_len < 4096)
+                       sirf_in_message[sirf_in_len++] = uc;
+               if (uc == 0xb3) {
+                       check_sirf_message("recv", sirf_in_message, sirf_in_len);
+                       sirf_in_len = 0;
+               }
+       }
+       return c;
+}
+
+
+void
+ao_serial1_putchar(char c)
+{
+       int     i;
+       uint8_t uc = (uint8_t) c;
+
+       if (sirf_message_len || uc == 0xa0) {
+               if (sirf_message_len < 4096)
+                       sirf_message[sirf_message_len++] = uc;
+               if (uc == 0xb3) {
+                       check_sirf_message("send", sirf_message, sirf_message_len);
+                       sirf_message_len = 0;
+               }
+       }
+       for (;;) {
+               i = write(ao_gps_fd, &c, 1);
+               if (i == 1) {
+                       if ((uint8_t) c == 0xb3 || c == '\r') {
+/*                             static const struct timespec delay = {
+                                       .tv_sec = 0,
+                                       .tv_nsec = 100 * 1000 * 1000
+                               };
+*/
+                               tcdrain(ao_gps_fd);
+//                             nanosleep(&delay, NULL);
+                       }
+                       break;
+               }
+               if (i < 0 && (errno == EINTR || errno == EAGAIN))
+                       continue;
+               perror("putchar");
+               exit(1);
+       }
+}
+
+#define AO_SERIAL_SPEED_4800   0
+#define AO_SERIAL_SPEED_57600  1
+
+static void
+ao_serial1_set_speed(uint8_t speed)
+{
+       int     fd = ao_gps_fd;
+       struct termios  termios;
+
+       tcdrain(fd);
+       tcgetattr(fd, &termios);
+       switch (speed) {
+       case AO_SERIAL_SPEED_4800:
+               cfsetspeed(&termios, B4800);
+               break;
+       case AO_SERIAL_SPEED_57600:
+               cfsetspeed(&termios, B57600);
+               break;
+       }
+       tcsetattr(fd, TCSAFLUSH, &termios);
+       tcflush(fd, TCIFLUSH);
+}
+
+#define ao_time() 0
+
+#include "ao_gps_print.c"
+#include "ao_gps_sirf.c"
+
+void
+ao_dump_state(void *wchan)
+{
+       double  lat, lon;
+       int     i;
+       if (wchan == &ao_gps_data)
+               ao_gps_print(&ao_gps_data);
+       else
+               ao_gps_tracking_print(&ao_gps_tracking_data);
+       putchar('\n');
+       return;
+       printf ("%02d:%02d:%02d",
+               ao_gps_data.hour, ao_gps_data.minute,
+               ao_gps_data.second);
+       printf (" nsat %d %svalid",
+               ao_gps_data.flags & AO_GPS_NUM_SAT_MASK,
+               ao_gps_data.flags & AO_GPS_VALID ? "" : "not ");
+       printf (" lat %g lon %g alt %d",
+               ao_gps_data.latitude / 1.0e7,
+               ao_gps_data.longitude / 1.0e7,
+               ao_gps_data.altitude);
+       printf (" speed %g climb %g course %d",
+               ao_gps_data.ground_speed / 100.0,
+               ao_gps_data.climb_rate / 100.0,
+               ao_gps_data.course * 2);
+       printf (" hdop %g h_error %d v_error %d",
+               ao_gps_data.hdop / 5.0,
+               ao_gps_data.h_error, ao_gps_data.v_error);
+       printf("\n");
+       printf ("\t");
+       for (i = 0; i < 12; i++)
+               printf (" %2d(%02d)",
+                       ao_gps_tracking_data.sats[i].svid,
+                       ao_gps_tracking_data.sats[i].c_n_1);
+       printf ("\n");
+}
+
+int
+ao_gps_open(const char *tty)
+{
+       struct termios  termios;
+       int fd;
+
+       fd = open (tty, O_RDWR);
+       if (fd < 0)
+               return -1;
+
+       tcgetattr(fd, &termios);
+       cfmakeraw(&termios);
+       cfsetspeed(&termios, B4800);
+       tcsetattr(fd, TCSAFLUSH, &termios);
+
+       tcdrain(fd);
+       tcflush(fd, TCIFLUSH);
+       return fd;
+}
+
+#include <getopt.h>
+
+static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--tty <tty-name>]\n", program);
+       exit(1);
+}
+
+int
+main (int argc, char **argv)
+{
+       char    *tty = "/dev/ttyUSB0";
+       int     c;
+
+       while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) {
+               switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+       ao_gps_fd = ao_gps_open(tty);
+       if (ao_gps_fd < 0) {
+               perror (tty);
+               exit (1);
+       }
+       ao_gps_setup();
+       ao_gps();
+}
diff --git a/src/test/ao_gps_test_skytraq.c b/src/test/ao_gps_test_skytraq.c
new file mode 100644 (file)
index 0000000..846daa9
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * Copyright © 2009 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#define AO_GPS_TEST
+#include "ao_host.h"
+#include <termios.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define AO_GPS_NUM_SAT_MASK    (0xf << 0)
+#define AO_GPS_NUM_SAT_SHIFT   (0)
+
+#define AO_GPS_VALID           (1 << 4)
+#define AO_GPS_RUNNING         (1 << 5)
+#define AO_GPS_DATE_VALID      (1 << 6)
+#define AO_GPS_COURSE_VALID    (1 << 7)
+
+struct ao_gps_orig {
+       uint8_t                 year;
+       uint8_t                 month;
+       uint8_t                 day;
+       uint8_t                 hour;
+       uint8_t                 minute;
+       uint8_t                 second;
+       uint8_t                 flags;
+       int32_t                 latitude;       /* degrees * 10⁷ */
+       int32_t                 longitude;      /* degrees * 10⁷ */
+       int16_t                 altitude;       /* m */
+       uint16_t                ground_speed;   /* cm/s */
+       uint8_t                 course;         /* degrees / 2 */
+       uint8_t                 hdop;           /* * 5 */
+       int16_t                 climb_rate;     /* cm/s */
+       uint16_t                h_error;        /* m */
+       uint16_t                v_error;        /* m */
+};
+
+#define SIRF_SAT_STATE_ACQUIRED                        (1 << 0)
+#define SIRF_SAT_STATE_CARRIER_PHASE_VALID     (1 << 1)
+#define SIRF_SAT_BIT_SYNC_COMPLETE             (1 << 2)
+#define SIRF_SAT_SUBFRAME_SYNC_COMPLETE                (1 << 3)
+#define SIRF_SAT_CARRIER_PULLIN_COMPLETE       (1 << 4)
+#define SIRF_SAT_CODE_LOCKED                   (1 << 5)
+#define SIRF_SAT_ACQUISITION_FAILED            (1 << 6)
+#define SIRF_SAT_EPHEMERIS_AVAILABLE           (1 << 7)
+
+struct ao_gps_sat_orig {
+       uint8_t         svid;
+       uint8_t         c_n_1;
+};
+
+#define AO_MAX_GPS_TRACKING    12
+
+struct ao_gps_tracking_orig {
+       uint8_t                 channels;
+       struct ao_gps_sat_orig  sats[AO_MAX_GPS_TRACKING];
+};
+
+#define ao_telemetry_location ao_gps_orig
+#define ao_telemetry_satellite ao_gps_tracking_orig
+#define ao_telemetry_satellite_info ao_gps_sat_orig
+
+void
+ao_mutex_get(uint8_t *mutex)
+{
+}
+
+void
+ao_mutex_put(uint8_t *mutex)
+{
+}
+
+static int
+ao_gps_fd;
+
+#if 0
+static void
+ao_dbg_char(char c)
+{
+       char    line[128];
+       line[0] = '\0';
+       if (c < ' ') {
+               if (c == '\n')
+                       sprintf (line, "\n");
+               else
+                       sprintf (line, "\\%02x", ((int) c) & 0xff);
+       } else {
+               sprintf (line, "%c", c);
+       }
+       write(1, line, strlen(line));
+}
+#endif
+
+#define QUEUE_LEN      4096
+
+static char    input_queue[QUEUE_LEN];
+int            input_head, input_tail;
+
+#include <sys/time.h>
+
+int
+get_millis(void)
+{
+       struct timeval  tv;
+       gettimeofday(&tv, NULL);
+       return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+}
+
+static void
+check_skytraq_message(char *from, uint8_t *msg, int len)
+{
+       uint16_t        encoded_len, encoded_cksum;
+       uint16_t        cksum;
+       uint8_t         id;
+       int             i;
+
+//     fwrite(msg, 1, len, stdout);
+       return;
+       if (msg[0] != 0xa0 || msg[1] != 0xa2) {
+               printf ("bad header\n");
+               return;
+       }
+       if (len < 7) {
+               printf("short\n");
+               return;
+       }
+       if (msg[len-1] != 0xb3 || msg[len-2] != 0xb0) {
+               printf ("bad trailer\n");
+               return;
+       }
+       encoded_len = (msg[2] << 8) | msg[3];
+       id = msg[4];
+/*     printf ("%9d: %3d\n", get_millis(), id); */
+       if (encoded_len != len - 8) {
+               if (id != 52)
+                       printf ("length mismatch (got %d, wanted %d)\n",
+                               len - 8, encoded_len);
+               return;
+       }
+       encoded_cksum = (msg[len - 4] << 8) | msg[len-3];
+       cksum = 0;
+       for (i = 4; i < len - 4; i++)
+               cksum = (cksum + msg[i]) & 0x7fff;
+       if (encoded_cksum != cksum) {
+               printf ("cksum mismatch (got %04x wanted %04x)\n",
+                       cksum, encoded_cksum);
+               return;
+       }
+       id = msg[4];
+       switch (id) {
+       case 41:{
+               int     off = 4;
+
+               uint8_t         id;
+               uint16_t        nav_valid;
+               uint16_t        nav_type;
+               uint16_t        week;
+               uint32_t        tow;
+               uint16_t        year;
+               uint8_t         month;
+               uint8_t         day;
+               uint8_t         hour;
+               uint8_t         minute;
+               uint16_t        second;
+               uint32_t        sat_list;
+               int32_t         lat;
+               int32_t         lon;
+               int32_t         alt_ell;
+               int32_t         alt_msl;
+               int8_t          datum;
+               uint16_t        sog;
+               uint16_t        cog;
+               int16_t         mag_var;
+               int16_t         climb_rate;
+               int16_t         heading_rate;
+               uint32_t        h_error;
+               uint32_t        v_error;
+               uint32_t        t_error;
+               uint16_t        h_v_error;
+
+#define get_u8(u)      u = (msg[off]); off+= 1
+#define get_u16(u)     u = (msg[off] << 8) | (msg[off + 1]); off+= 2
+#define get_u32(u)     u = (msg[off] << 24) | (msg[off + 1] << 16) | (msg[off+2] << 8) | (msg[off+3]); off+= 4
+
+               get_u8(id);
+               get_u16(nav_valid);
+               get_u16(nav_type);
+               get_u16(week);
+               get_u32(tow);
+               get_u16(year);
+               get_u8(month);
+               get_u8(day);
+               get_u8(hour);
+               get_u8(minute);
+               get_u16(second);
+               get_u32(sat_list);
+               get_u32(lat);
+               get_u32(lon);
+               get_u32(alt_ell);
+               get_u32(alt_msl);
+               get_u8(datum);
+               get_u16(sog);
+               get_u16(cog);
+               get_u16(mag_var);
+               get_u16(climb_rate);
+               get_u16(heading_rate);
+               get_u32(h_error);
+               get_u32(v_error);
+               get_u32(t_error);
+               get_u16(h_v_error);
+
+
+               (void) mag_var;
+               (void) id;
+               printf ("Geodetic Navigation Data (41):\n");
+               printf ("\tNav valid %04x\n", nav_valid);
+               printf ("\tNav type %04x\n", nav_type);
+               printf ("\tWeek %5d", week);
+               printf (" TOW %9d", tow);
+               printf (" %4d-%2d-%2d %02d:%02d:%07.4f\n",
+                       year, month, day,
+                       hour, minute, second / 1000.0);
+               printf ("\tsats: %08x\n", sat_list);
+               printf ("\tlat: %g", lat / 1.0e7);
+               printf (" lon: %g", lon / 1.0e7);
+               printf (" alt_ell: %g", alt_ell / 100.0);
+               printf (" alt_msll: %g", alt_msl / 100.0);
+               printf (" datum: %d\n", datum);
+               printf ("\tground speed: %g", sog / 100.0);
+               printf (" course: %g", cog / 100.0);
+               printf (" climb: %g", climb_rate / 100.0);
+               printf (" heading rate: %g\n", heading_rate / 100.0);
+               printf ("\th error: %g", h_error / 100.0);
+               printf (" v error: %g", v_error / 100.0);
+               printf (" t error: %g", t_error / 100.0);
+               printf (" h vel error: %g\n", h_v_error / 100.0);
+               break;
+       }
+       case 4: {
+               int off = 4;
+               uint8_t         id;
+               int16_t         gps_week;
+               uint32_t        gps_tow;
+               uint8_t         channels;
+               int             j, k;
+
+               get_u8(id);
+               get_u16(gps_week);
+               get_u32(gps_tow);
+               get_u8(channels);
+
+               (void) id;
+               printf ("Measured Tracker Data (4):\n");
+               printf ("GPS week: %d\n", gps_week);
+               printf ("GPS time of week: %d\n", gps_tow);
+               printf ("channels: %d\n", channels);
+               for (j = 0; j < 12; j++) {
+                       uint8_t svid, azimuth, elevation;
+                       uint16_t state;
+                       uint8_t c_n[10];
+                       get_u8(svid);
+                       get_u8(azimuth);
+                       get_u8(elevation);
+                       get_u16(state);
+                       for (k = 0; k < 10; k++) {
+                               get_u8(c_n[k]);
+                       }
+                       printf ("Sat %3d:", svid);
+                       printf (" aziumuth: %6.1f", azimuth * 1.5);
+                       printf (" elevation: %6.1f", elevation * 0.5);
+                       printf (" state: 0x%02x", state);
+                       printf (" c_n:");
+                       for (k = 0; k < 10; k++)
+                               printf(" %3d", c_n[k]);
+                       if (state & SIRF_SAT_STATE_ACQUIRED)
+                               printf(" acq,");
+                       if (state & SIRF_SAT_STATE_CARRIER_PHASE_VALID)
+                               printf(" car,");
+                       if (state & SIRF_SAT_BIT_SYNC_COMPLETE)
+                               printf(" bit,");
+                       if (state & SIRF_SAT_SUBFRAME_SYNC_COMPLETE)
+                               printf(" sub,");
+                       if (state & SIRF_SAT_CARRIER_PULLIN_COMPLETE)
+                               printf(" pullin,");
+                       if (state & SIRF_SAT_CODE_LOCKED)
+                               printf(" code,");
+                       if (state & SIRF_SAT_ACQUISITION_FAILED)
+                               printf(" fail,");
+                       if (state & SIRF_SAT_EPHEMERIS_AVAILABLE)
+                               printf(" ephem,");
+                       printf ("\n");
+               }
+               break;
+       }
+       default:
+               return;
+               printf ("%s %4d:", from, encoded_len);
+               for (i = 4; i < len - 4; i++) {
+                       if (((i - 4) & 0xf) == 0)
+                               printf("\n   ");
+                       printf (" %3d", msg[i]);
+               }
+               printf ("\n");
+       }
+}
+
+static uint8_t skytraq_message[4096];
+static int     skytraq_message_len;
+static uint8_t skytraq_in_message[4096];
+static int     skytraq_in_len;
+
+char
+ao_serial1_getchar(void)
+{
+       char    c;
+       uint8_t uc;
+
+       while (input_head == input_tail) {
+               for (;;) {
+                       input_tail = read(ao_gps_fd, input_queue, QUEUE_LEN);
+                       if (input_tail < 0) {
+                               if (errno == EINTR || errno == EAGAIN)
+                                       continue;
+                               perror ("getchar");
+                               exit (1);
+                       }
+                       input_head = 0;
+                       break;
+               }
+       }
+       c = input_queue[input_head];
+       input_head = (input_head + 1) % QUEUE_LEN;
+       uc = c;
+//     printf ("c: %02x %c\n", uc, uc);
+       if (skytraq_in_len || uc == '$') {
+               if (skytraq_in_len < 4096)
+                       skytraq_in_message[skytraq_in_len++] = uc;
+               if (uc == 0x0a) {
+                       check_skytraq_message("recv", skytraq_in_message, skytraq_in_len);
+                       skytraq_in_len = 0;
+               }
+       }
+       return c;
+}
+
+
+void
+ao_serial1_putchar(char c)
+{
+       int     i;
+       uint8_t uc = (uint8_t) c;
+
+       if (skytraq_message_len || uc == 0xa0) {
+               if (skytraq_message_len < 4096)
+                       skytraq_message[skytraq_message_len++] = uc;
+               if (uc == 0x0a) {
+                       check_skytraq_message("send", skytraq_message, skytraq_message_len);
+                       skytraq_message_len = 0;
+               }
+       }
+       for (;;) {
+               i = write(ao_gps_fd, &c, 1);
+               if (i == 1) {
+                       if ((uint8_t) c == 0xb3 || c == '\r') {
+/*                             static const struct timespec delay = {
+                                       .tv_sec = 0,
+                                       .tv_nsec = 100 * 1000 * 1000
+                               };
+*/
+                               tcdrain(ao_gps_fd);
+//                             nanosleep(&delay, NULL);
+                       }
+                       break;
+               }
+               if (i < 0 && (errno == EINTR || errno == EAGAIN))
+                       continue;
+               perror("putchar");
+               exit(1);
+       }
+}
+
+#define AO_SERIAL_SPEED_4800   0
+#define AO_SERIAL_SPEED_9600   1
+#define AO_SERIAL_SPEED_57600  2
+
+static void
+ao_serial1_set_speed(uint8_t speed)
+{
+       int     fd = ao_gps_fd;
+       struct termios  termios;
+
+       tcdrain(fd);
+       tcgetattr(fd, &termios);
+       switch (speed) {
+       case AO_SERIAL_SPEED_4800:
+               cfsetspeed(&termios, B4800);
+               break;
+       case AO_SERIAL_SPEED_9600:
+               cfsetspeed(&termios, B38400);
+               break;
+       case AO_SERIAL_SPEED_57600:
+               cfsetspeed(&termios, B57600);
+               break;
+       }
+       tcsetattr(fd, TCSAFLUSH, &termios);
+       tcflush(fd, TCIFLUSH);
+}
+
+#define ao_time() 0
+
+#include "ao_gps_print.c"
+#include "ao_gps_skytraq.c"
+
+void
+ao_dump_state(void *wchan)
+{
+       if (wchan == &ao_gps_data)
+               ao_gps_print(&ao_gps_data);
+       else
+               ao_gps_tracking_print(&ao_gps_tracking_data);
+       putchar('\n');
+       return;
+}
+
+int
+ao_gps_open(const char *tty)
+{
+       struct termios  termios;
+       int fd;
+
+       fd = open (tty, O_RDWR);
+       if (fd < 0)
+               return -1;
+
+       tcgetattr(fd, &termios);
+       cfmakeraw(&termios);
+       cfsetspeed(&termios, B4800);
+       tcsetattr(fd, TCSAFLUSH, &termios);
+
+       tcdrain(fd);
+       tcflush(fd, TCIFLUSH);
+       return fd;
+}
+
+#include <getopt.h>
+
+static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--tty <tty-name>]\n", program);
+       exit(1);
+}
+
+int
+main (int argc, char **argv)
+{
+       char    *tty = "/dev/ttyUSB0";
+       int     c;
+
+       while ((c = getopt_long(argc, argv, "T:", options, NULL)) != -1) {
+               switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+       ao_gps_fd = ao_gps_open(tty);
+       if (ao_gps_fd < 0) {
+               perror (tty);
+               exit (1);
+       }
+       ao_gps();
+       return 0;
+}
diff --git a/src/test/plottest b/src/test/plottest
new file mode 100755 (executable)
index 0000000..76af5ee
--- /dev/null
@@ -0,0 +1,16 @@
+gnuplot -persist << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+plot "$1" using 1:3 with lines axes x1y1 title "raw height",\
+"$1" using 1:5 with lines axes x1y2 title "raw accel",\
+"$1" using 1:9 with lines axes x1y1 title "height",\
+"$1" using 1:11 with lines axes x1y2 title "speed",\
+"$1" using 1:13 with lines axes x1y2 title "accel",\
+"$1" using 1:15 with lines axes x1y1 title "drogue",\
+"$1" using 1:17 with lines axes x1y1 title "main",\
+"$1" using 1:19 with lines axes x1y1 title "error"
+EOF
diff --git a/src/test/run-baro b/src/test/run-baro
new file mode 100755 (executable)
index 0000000..21bab9f
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+for i in "$@"; do
+./ao_flight_test_baro "$i" > run-out.baro
+
+#./ao_flight_test_accel "$i" > run-out.accel
+#"run-out.accel" using 1:9 with lines lt 4 axes x1y1 title "accel height",\
+#"run-out.accel" using 1:11 with lines lt 4 axes x1y2 title "accel speed",\
+#"run-out.accel" using 1:13 with lines lt 4 axes x1y2 title "accel accel",\
+#"run-out.accel" using 1:15 with lines lt 4 axes x1y1 title "accel drogue",\
+#"run-out.accel" using 1:17 with lines lt 4 axes x1y1 title "accel main",\
+#
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.baro" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.baro" using 1:5 with lines lw 2 lt 2 axes x1y2 title "raw accel",\
+"run-out.baro" using 1:9 with lines lt 3 axes x1y1 title "baro height",\
+"run-out.baro" using 1:11 with lines lt 4 axes x1y2 title "baro speed",\
+"run-out.baro" using 1:13 with lines lt 5 axes x1y2 title "baro accel",\
+"run-out.baro" using 1:17 with lines lt 6 axes x1y1 title "baro drogue",\
+"run-out.baro" using 1:19 with lines lt 7 axes x1y1 title "baro main"
+pause mouse close
+EOF
+done
\ No newline at end of file
diff --git a/src/test/run-full b/src/test/run-full
new file mode 100755 (executable)
index 0000000..50ab7b5
--- /dev/null
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+for i in "$@"; do
+./ao_flight_test "$i" > run-out.full
+
+#./ao_flight_test_accel "$i" > run-out.accel
+#"run-out.accel" using 1:9 with lines lt 4 axes x1y1 title "accel height",\
+#"run-out.accel" using 1:11 with lines lt 4 axes x1y2 title "accel speed",\
+#"run-out.accel" using 1:13 with lines lt 4 axes x1y2 title "accel accel",\
+#"run-out.accel" using 1:15 with lines lt 4 axes x1y1 title "accel drogue",\
+#"run-out.accel" using 1:17 with lines lt 4 axes x1y1 title "accel main",\
+#
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.full" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.full" using 1:5 with lines lw 2 lt 2 axes x1y2 title "raw accel",\
+"run-out.full" using 1:9 with lines lt 3 axes x1y1 title "full height",\
+"run-out.full" using 1:11 with lines lt 4 axes x1y2 title "full speed",\
+"run-out.full" using 1:13 with lines lt 5 axes x1y2 title "full accel",\
+"run-out.full" using 1:17 with lines lt 6 axes x1y1 title "full drogue",\
+"run-out.full" using 1:19 with lines lt 7 axes x1y1 title "full main"
+pause mouse close
+EOF
+done
\ No newline at end of file
diff --git a/src/test/run-noisy b/src/test/run-noisy
new file mode 100755 (executable)
index 0000000..2e3cddc
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+for i in "$@"; do
+./ao_flight_test_noisy_accel "$i" > run-out.noisy
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.noisy" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.noisy" using 1:5 with lines lw 2 lt 2 axes x1y2 title "raw accel",\
+"run-out.noisy" using 1:9 with lines lt 3 axes x1y1 title "noisy height",\
+"run-out.noisy" using 1:11 with lines lt 4 axes x1y2 title "noisy speed",\
+"run-out.noisy" using 1:13 with lines lt 5 axes x1y2 title "noisy accel",\
+"run-out.noisy" using 1:17 with lines lt 6 axes x1y1 title "noisy drogue",\
+"run-out.noisy" using 1:19 with lines lt 7 axes x1y1 title "noisy main"
+pause mouse close
+EOF
+done
\ No newline at end of file
diff --git a/src/test/run-one b/src/test/run-one
new file mode 100755 (executable)
index 0000000..8fa0787
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+DIR=~/misc/rockets/flights
+
+for i in "$@"; do
+case "$i" in
+    */*)
+    file="$i"
+    ;;
+    *)
+    file="$DIR/$i"
+    ;;
+esac
+./ao_flight_test "$file" > run-out.full
+./ao_flight_test_baro "$file" > run-out.baro
+
+#./ao_flight_test_accel "$file" > run-out.accel
+#"run-out.accel" using 1:9 with lines lt 4 axes x1y1 title "accel height",\
+#"run-out.accel" using 1:11 with lines lt 4 axes x1y2 title "accel speed",\
+#"run-out.accel" using 1:13 with lines lt 4 axes x1y2 title "accel accel",\
+#"run-out.accel" using 1:15 with lines lt 4 axes x1y1 title "accel drogue",\
+#"run-out.accel" using 1:17 with lines lt 4 axes x1y1 title "accel main",\
+#
+
+gnuplot << EOF
+set ylabel "altitude (m)"
+set y2label "velocity (m/s), acceleration(m/s²)"
+set xlabel "time (s)"
+set xtics border out nomirror
+set ytics border out nomirror
+set y2tics border out nomirror
+set title "$i"
+plot "run-out.full" using 1:3 with lines lw 2 lt 1 axes x1y1 title "raw height",\
+"run-out.full" using 1:5 with lines lw 2 lt 1 axes x1y2 title "raw accel",\
+"run-out.full" using 1:9 with lines lt 2 axes x1y1 title "full height",\
+"run-out.full" using 1:11 with lines lt 2 axes x1y2 title "full speed",\
+"run-out.full" using 1:13 with lines lt 2 axes x1y2 title "full accel",\
+"run-out.full" using 1:15 with lines lt 2 axes x1y1 title "full drogue",\
+"run-out.full" using 1:17 with lines lt 2 axes x1y1 title "full main", \
+"run-out.baro" using 1:9 with lines lt 3 axes x1y1 title "baro height",\
+"run-out.baro" using 1:11 with lines lt 3 axes x1y2 title "baro speed",\
+"run-out.baro" using 1:13 with lines lt 3 axes x1y2 title "baro accel",\
+"run-out.baro" using 1:15 with lines lt 3 axes x1y1 title "baro drogue",\
+"run-out.baro" using 1:17 with lines lt 3 axes x1y1 title "baro main"
+pause mouse close
+EOF
+done
\ No newline at end of file
diff --git a/src/test/run-tests b/src/test/run-tests
new file mode 100755 (executable)
index 0000000..4edecf1
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+DIR=~/misc/rockets/flights
+
+expect_baro=2
+expect_full=5
+
+bad_baro=0
+bad_full=0
+while read flight description; do
+    if ./ao_flight_test_baro -s -i "$description" $DIR/$flight; then
+       :
+    else
+       : $((bad_baro++))
+    fi
+    if ./ao_flight_test -s -i "$description" $DIR/$flight; then
+       :
+    else
+       : $((bad_full++))
+    fi
+done < test-flights
+echo baro errors $bad_baro expected $expect_baro
+echo full errors $bad_full expected $expect_full
+: $((miss_baro = bad_baro > expect_baro ? bad_baro - expect_baro : expect_baro - bad_baro))
+: $((miss_full = bad_full > expect_full ? bad_full - expect_full : expect_full - bad_full))
+: $((miss = miss_baro + miss_full))
+exit $miss
\ No newline at end of file
diff --git a/src/test/test-flights b/src/test/test-flights
new file mode 100644 (file)
index 0000000..12f62ea
--- /dev/null
@@ -0,0 +1,70 @@
+2009-06-03-serial-003-flight-002.eeprom PSAS flight on research motor.Lots of ugly bumps.
+2009-06-05-serial-004-flight-008.eeprom Add June 2009 Lil Nuke flight
+2009-06-06-serial-004-flight-009.eeprom Another Lil nuke flight? 700m, subsonic.
+2009-06-14-serial-004-flight-010.eeprom Lil nuke flies again. 450m, subsonic.
+2009-06-20-serial-003-flight-003.eeprom Transonic flight. 2100m, single deploy.
+2009-07-04-serial-001-flight-006.eeprom Subsonic, 1100m, noisy descent.
+2009-07-18-serial-001-flight-007.eeprom Transonic, 1900m, noisy descent.
+2009-08-22-serial-001-flight-001.eeprom Transonic 2200m. noisy descent
+2009-08-22-serial-010-flight-001.eeprom Subsonic, laggy baro. 400m
+2009-09-05-serial-010-flight-002.eeprom Subsonic, fairly quick baro. 600m
+2009-09-05-serial-011-flight-001.eeprom Subsonic, 1800m. Early Ejection confuses apogee detect.
+2009-09-11-serial-008-flight-003.eeprom Subsonic, 200m, early ejection.
+2009-09-12-serial-002-flight-002.eeprom Subsonic. 1200m. Very nice baro velocity
+2009-09-12-serial-008-flight-005.eeprom Low flight. 220m, 100m/s.
+2009-09-12-serial-008-flight-007.eeprom Model flight.
+2009-09-19-serial-011-flight-002.eeprom Subsonic, but noisy baro.
+2009-10-03-serial-008-flight-009.eeprom Transonic baro bump. 1600m.
+2009-10-03-serial-008-flight-010.eeprom Barely transonic, but other baro bumps.
+2009-10-03-serial-009-flight-001.eeprom Add Ofest ToT flight (drag race with Candy Cane). subsonic, but a bit of baro noise.
+2009-10-03-serial-011-flight-003.eeprom Add Ofest G-spot flight. transonic bump, ugly looking data.
+2009-10-17-serial-009-flight-002.eeprom Clean looking subsonic flight, small baro slump at max-Q
+2009-11-14-serial-003-flight-005.eeprom Model flight.
+2009-11-14-serial-003-flight-006.eeprom Model flight.
+2009-11-21-serial-013-flight-001.eeprom ToT AMW J450ST with Jason Chamberlin strontium nitrate igniter
+2009-11-21-serial-013-flight-002.eeprom ToT - CTI 1009J420CL pro38 6xl
+2010-02-13-serial-051-flight-002.eeprom G-Spot at Albuquerque Rocket Society launch site in Rio Rancho, NM Cesaroni 229H255WT-14A
+2010-02-13-serial-052-flight-002.eeprom Add Grappler flight at OROC February model launch
+2010-02-28-serial-052-flight-003.eeprom LDDD Tillamook airport, 2010-02-28, H252
+2010-02-28-serial-052-flight-004.eeprom LDDD Tillamook airport, 2010-02-28, I161
+2010-03-06-serial-010-flight-004.eeprom Robert's Lil Nuke at Hudson Ranch on a CTI 108G57CL Pro29
+2010-03-06-serial-013-flight-003.eeprom Trick-O-Treat at Hudson Ranch on a J595BS
+2010-03-06-serial-051-flight-003.eeprom G-Spot at Hudson Ranch on an H180W (apogee early, fools ao_flight_test_baro)
+2010-03-06-serial-053-flight-004.eeprom Horizon Rebuilt at Hudson Ranch on a J530IM with MAWD as backup
+2010-05-08-serial-229-flight-002.eeprom First customer flight on a sparky.
+2010-05-28-serial-215-flight-002.eeprom Mike's L1 cert flight. Congrats!
+2010-05-28-serial-224-flight-001.eeprom Bill Mott's amram
+2010-05-29-serial-052-flight-005.eeprom LDDD on J335 at MHM
+2010-05-30-serial-010-flight-005.eeprom Robert's Lil Nuke on a 84G88 Smoky Sam at Mile High Mayhem 2010 Version 0.1 TeleMetrum with 5010 GPS board.
+2010-05-30-serial-051-flight-004.eeprom Bdale's G-Spot on a CTI 298H159 Green3 at Mile High Mayhem 2010
+2010-05-30-serial-224-flight-003.eeprom Bill Mott's L3 cert flight
+2010-05-30-serial-226-flight-001.eeprom Robert's RG-2 on a 159G54RL at Mile High Mayhem. Very noise accel data, early apogee (fools full)
+2010-05-31-serial-010-flight-006.eeprom Robert's Lil Nuke with a v0.1 TeleMetrum and 5010 GPS board flying on a 159G118 Blue Streak on the last day of Mile High Mayhem 2010
+2010-05-31-serial-216-flight-001.eeprom Sharp Stick in the Sky on an "I something" EX motor from James Russell
+2010-05-31-serial-219-flight-001.eeprom Candy Cane on CTI K300 at MHM. (accel noise fools full)
+2010-05-31-serial-227-flight-003.eeprom Mike's Rocket on J285 for successful L2 cert at MHM
+2010-06-05-serial-220-flight-001.eeprom Mini-mmuchness on 159G54RL, Sunday at MHM 2010
+2010-06-26-serial-209-flight-003.eeprom Tripoli Colorado Spring Fling with COSROCS at the Buffalo Ranch
+2010-06-26-serial-215-flight-004.eeprom Tripoli Colorado Spring Fling with COSROCS at the Buffalo Ranch (accel noise fools full)
+2010-06-26-serial-220-flight-002.eeprom Mini Mmuchness on CTI H120CL to 1975m, OROC June 2010
+2010-06-26-serial-226-flight-002.eeprom Tripoli Colorado Spring Fling with COSROCS at the Buffalo Ranch
+2010-06-27-serial-221-flight-002.eeprom PSAS LV2c on N2000 at OROC june launch
+2010-07-17-serial-230-flight-001.eeprom Mike Ward's Level 1 cert flight on H225
+2010-07-18-serial-219-flight-002.eeprom MMuchness on M1230 for successful L3 cert flight
+2010-08-07-serial-216-flight-003.eeprom Sharp stick on I300T at Metra's august launch. Main out at apogee.
+2010-08-07-serial-220-flight-003.eeprom Mini-mmuchness flight at Metra after debconf. Unstable on G80. ABNORMAL FLIGHT. (fools full)
+2010-08-12-serial-236-flight-001.eeprom Edgar's L1 flight, Madcow Momba on Aerotech H128 (early apogee charge fools baro)
+2010-08-21-serial-010-flight-007.eeprom Robert's Lil Nuke, flying on a CTI Pro29 2-grain 110G250 Vmax. Just awesome!  PFND.
+2010-08-21-serial-224-flight-004.eeprom Anthony Towns' LDDD clone, successful L1 cert on a CTI Pro38 2-grain 266H125 classic.  Airframe was set up for apogee-only with motor based ejection.  The BP charge was larger than necessary, caused nose cone to snap back against aft airframe.  Minor damage to leading edge of aft airframe, big chunk of the nose cone skin cracked away.  Same problem Bdale saw on Sharp Stick's nosecone, also from Performance Rocketry, where there was apparently an air bubble below the gel coat.  Determination was that damage was cosmetic and would not affect cert... confirmed by successful L2 later in the day!
+2010-08-21-serial-224-flight-005.eeprom Anthony Towns' LDDD clone, successful L2 cert on a CTI Pro38 5-grain 58J357 blue streak.  Perfect drogueless dual deploy!  0.8-0.9 grams aft charge and about 1.0 grams forward.  Flew without ground testing charges due to time pressure before waiver closed for the day.  About as close to the waiver as you'd ever want to be on a cert flight, again demonstrating that OpenRocket under-estimates apogee ... approximately 6800 ft predicted...
+2010-08-21-serial-233-flight-002.eeprom Tim van Milligan's 5.5" L2 airframe, flying on a CTI Pro38 6-grain 774J410 red, TM indicated a problem with the apogee igniter continuity, noticed during countdown, turned out to be a loose wire!  Perfect dual deploy flight once that was fixed.
+2010-09-03-serial-051-flight-005.eeprom G-spot on an old Aerotech H125-20W single use 29mm motor.  It appears the ejection at apogee actually happened much later, perhaps as much as 10 seconds late!
+2010-09-03-serial-215-flight-005.eeprom Horizon Rebuild on a K490 Green3 reload.  PFND. About 1.4 grams each end. (accel noise fools full)
+2010-09-24-serial-236-flight-006.eeprom LDDD on I236 in Sheridan on 2010-09-24 to 1216m
+2010-09-25-serial-223-flight-001.eeprom Bdale's 10" Goblin, flying on a CTI Pro75 M1300 Imax dual thrust during Chili Blaster 2 at Hudson Ranch.  First flight after rebuild to do dual deploy from one bay with ARRD and 15' surplus "+ sign" parachute.
+2010-10-17-serial-215-flight-006.eeprom Horizon Rebuild on CTI J595BS at Tripoli Colorado launch site near Hartsel, CO
+2011-01-30-serial-056-flight-001.eeprom Group project 5.5" Polecat Thumper airframe built in the Quay West Suites in Brisbane, Australia, during LCA 2011 by Bdale, Keith, Mike Beattie, and Anthony Towns.  Flown at QRS club launch on an Aerotech J315R 54mm 2-grain motor.
+2011-01-30-serial-250-flight-002.eeprom Group project 5.5" Polecat Thumper airframe built in the Quay West Suites in Brisbane, Australia, during LCA 2011 by Bdale, Keith, Mike Beattie, and Anthony Towns.  Flown at QRS club launch on an Aerotech J315R 54mm 2-grain motor.
+2011-02-19-serial-215-flight-007.eeprom Horizon Rebuild on a CTI Pro38 6xl J600R at Hudson Ranch 1.0 grams BP rear, 1.5 grams front Perfect drogueless dual deploy flight with no damage!
+2011-02-19-serial-216-flight-006.eeprom Sharp Stick on a CTI Pro38 3-grain I345WT at Hudson Ranch Added more kevlar to the aft end, flew with 1.3 grams BP rear and 1.0 front. Perfect drogueless dual deploy flight, and no damage!
+2011-02-19-serial-286-flight-001.eeprom Vertical Assault on a CTI Pro38 6xl J595BS at Hudson Ranch 1.5 grams BP rear, 1.8 grams BP front Perfect drogueless dual deploy flight, with no damage!
diff --git a/src/tidongle/.gitignore b/src/tidongle/.gitignore
new file mode 100644 (file)
index 0000000..3888a0f
--- /dev/null
@@ -0,0 +1,2 @@
+tidongle*
+ao_product.h
diff --git a/src/tidongle/Makefile b/src/tidongle/Makefile
new file mode 100644 (file)
index 0000000..1514c4d
--- /dev/null
@@ -0,0 +1,91 @@
+#
+# TIDongle build file
+#
+
+vpath %.c ..:../core:../cc1111:../drivers:../product
+vpath %.h ..:../core:../cc1111:../drivers:../product
+vpath ao-make-product.5c ../util
+
+ifndef VERSION
+include ../Version
+endif
+
+INC = \
+       ao.h \
+       ao_pins.h \
+       cc1111.h \
+       ao_product.h
+
+CORE_SRC = \
+       ao_cmd.c \
+       ao_config.c \
+       ao_gps_print.c \
+       ao_monitor.c \
+       ao_mutex.c \
+       ao_panic.c \
+       ao_rssi.c \
+       ao_state.c \
+       ao_stdio.c \
+       ao_task.c \
+       ao_freq.c
+
+CC1111_SRC = \
+       ao_dbg.c \
+       ao_dma.c \
+       ao_led.c \
+       ao_packet.c \
+       ao_packet_master.c \
+       ao_radio.c \
+       ao_romconfig.c \
+       ao_string.c \
+       ao_timer.c \
+       ao_usb.c \
+       _bp.c
+
+DRIVER_SRC =
+
+PRODUCT_SRC = \
+       ao_tidongle.c
+
+SRC = \
+       $(CORE_SRC) \
+       $(CC1111_SRC) \
+       $(DRIVER_SRC) \
+       $(PRODUCT_SRC)
+
+PROGNAME = tidongle
+PROG = $(PROGNAME)-$(VERSION).ihx
+PRODUCT=TIDongle
+PRODUCT_DEF=-DTIDONGLE
+IDPRODUCT=0x000a
+
+include ../cc1111/Makefile.cc1111
+
+NICKLE=nickle
+CHECK_STACK=sh ../util/check-stack
+
+V=0
+# The user has explicitly enabled quiet compilation.
+ifeq ($(V),0)
+quiet = @printf "  $1 $2 $@\n"; $($1)
+endif
+# Otherwise, print the full command line.
+quiet ?= $($1)
+
+all: ../$(PROG)
+
+../$(PROG): $(REL) Makefile
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(REL) && cp $(PROG) $(PMAP) ..
+       $(call quiet,CHECK_STACK) ../cc1111/ao_arch.h $(PMEM) || rm $@
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
+
+distclean:     clean
+
+clean: clean-cc1111
+
+install:
+
+uninstall:
+
diff --git a/src/util/ao-make-product.5c b/src/util/ao-make-product.5c
new file mode 100644 (file)
index 0000000..5f2eb8e
--- /dev/null
@@ -0,0 +1,103 @@
+#!/bin/sh
+
+autoimport ParseArgs;
+
+void
+write_ucs2(string a, string description)
+{
+       int len = String::length(a);
+
+       printf("/* %s */\n", description);
+       printf("#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2);
+       printf("#define AO_%s_STRING \"%s\"\n", description, a);
+       printf("#define AO_%s_UCS2", description);
+       for (int i = 0; i < len; i++) {
+               int     c = a[i];
+               if (i > 0)
+                       printf(",");
+               if (0x20 <= c && c < 128)
+                       printf(" '%c', 0", c);
+               else
+                       printf(" LE_WORD(0x%04x),", c);
+       }
+       printf("\n\n");
+}
+
+void
+write_string(string a, string description)
+{
+       printf ("/* %s */\n", description);
+       printf ("#define AO_%s_STRING \"%s\"\n", description, a);
+}
+
+void
+write_int(int a, string description)
+{
+       printf ("/* %s */\n", description);
+       printf ("#define AO_%s_NUMBER %d\n\n", description, a);
+}
+
+void
+write_hex(int a, string description)
+{
+       printf ("/* %s */\n", description);
+       printf ("#define AO_%s_NUMBER 0x%04x\n\n", description, a);
+}
+
+string manufacturer = "altusmetrum.org";
+string product = "TeleMetrum";
+string version = "0.0";
+int serial = 1;
+int user_argind = 0;
+int id_product = 0x000a;
+
+argdesc argd = {
+       .args = {
+               {
+                       .var = { .arg_string = &manufacturer },
+                       .abbr = 'm',
+                       .name = "manufacturer",
+                       .expr_name = "manf",
+                       .desc = "Manufacturer name." },
+               {
+                       .var = { .arg_string = &product },
+                       .abbr = 'p',
+                       .name = "product",
+                       .expr_name = "prod",
+                       .desc = "Product name." },
+               {
+                       .var = { .arg_int = &id_product },
+                       .abbr = 'i',
+                       .name = "id_product",
+                       .expr_name = "id_p",
+                       .desc = "Product ID." },
+               {
+                       .var = { .arg_int = &serial },
+                       .abbr = 's',
+                       .name = "serial",
+                       .expr_name = "number",
+                       .desc = "Serial number." },
+               {
+                       .var = { .arg_string = &version },
+                       .abbr = 'v',
+                       .name = "version",
+                       .expr_name = "string",
+                       .desc = "Program version." },
+       },
+       .prog_name = "usb descriptors",
+};
+
+void
+main()
+{
+       string[dim(argv)-1] nargv = {[n] = argv[n+1]};
+       parseargs(&argd, &nargv);
+       write_ucs2(manufacturer, "iManufacturer");
+       write_ucs2(product, "iProduct");
+       write_ucs2(sprintf("%06d", serial), "iSerial");
+       write_int(serial, "iSerial");
+       write_hex(id_product, "idProduct");
+       write_string(version, "iVersion");
+}
+
+main();
diff --git a/src/util/check-avr-mem b/src/util/check-avr-mem
new file mode 100644 (file)
index 0000000..7956f0a
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+nm "$@" |
+grep ' N _end' |
+awk '{ iram = strtonum("0x" $1) % 0x10000;
+if ( iram > 0xacf) {
+       printf ("%d bytes of ram more than %d available\n", iram, 0xacf);
+       exit(1);
+} else {
+       printf("%d bytes of ram\n", iram);
+       exit(0);
+} }'
diff --git a/src/util/check-stack b/src/util/check-stack
new file mode 100755 (executable)
index 0000000..f4cada2
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+HEADER=$1
+MEM=$2
+
+HEADER_STACK=`awk '/#define AO_STACK_START/ {print strtonum($3)}' $HEADER`
+MEM_STACK=`awk '/Stack starts at/ {print strtonum ($4)}' $MEM`
+XRAM_END=`awk '/EXTERNAL RAM/ { print strtonum ($4)}' $MEM`
+FLASH_END=`awk '/ROM\/EPROM\/FLASH/ { print strtonum ($3)}' $MEM`
+
+if [ "$HEADER_STACK" -lt "$MEM_STACK" ]; then
+        echo $MEM_STACK | awk '{ printf ("Set AO_STACK_START to at least 0x%x\n", $1); }'
+       exit 1
+fi
+if [ "$XRAM_END" -ge 65024 ]; then
+    echo $XRAM_END | awk '{ printf ("Uses too much XRAM, 0x%x >= 0x%x\n", $1, 65024); }'
+    exit 1
+fi
+if [ "$FLASH_END" -ge 32768 ]; then
+    echo $FLASH_END | awk '{ printf ("Uses too much FLASH, 0x%x >= 0x%x\n", $1, 32768); }'
+    exit 1
+fi
+
+exit 0
+
diff --git a/src/util/gps-cksum b/src/util/gps-cksum
new file mode 100755 (executable)
index 0000000..a08153b
--- /dev/null
@@ -0,0 +1,17 @@
+#!/usr/bin/env nickle
+
+int checksum(string a)
+{
+       int     c = 0;
+       for (int i = 0; i < String::length(a); i++)
+               c ^= a[i];
+       return c;
+}
+
+void main()
+{
+       for (int i = 1; i < dim(argv); i++)
+               printf ("$%s*%02x\n", argv[i], checksum(argv[i]));
+}
+
+main();
diff --git a/src/util/make-altitude b/src/util/make-altitude
new file mode 100644 (file)
index 0000000..716aa8a
--- /dev/null
@@ -0,0 +1,283 @@
+#!/usr/bin/nickle -f
+/*
+ * Pressure Sensor Model, version 1.1
+ *
+ * written by Holly Grimes
+ *
+ * Uses the International Standard Atmosphere as described in
+ *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
+ *    from the Portland State Aerospace Society, except that the atmosphere
+ *    is divided into layers with each layer having a different lapse rate.
+ *
+ * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
+ *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
+ *
+ * Height measurements use the local tangent plane.  The postive z-direction is up.
+ *
+ * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
+ *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
+ *   in Joules/(kilogram-Kelvin).
+ */
+
+const real GRAVITATIONAL_ACCELERATION = -9.80665;
+const real AIR_GAS_CONSTANT = 287.053;
+const int NUMBER_OF_LAYERS = 7;
+const real MAXIMUM_ALTITUDE = 84852;
+const real MINIMUM_PRESSURE = 0.3734;
+const real LAYER0_BASE_TEMPERATURE = 288.15;
+const real LAYER0_BASE_PRESSURE = 101325;
+
+/* lapse rate and base altitude for each layer in the atmosphere */
+const real[NUMBER_OF_LAYERS] lapse_rate = {
+       -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
+};
+const int[NUMBER_OF_LAYERS] base_altitude = {
+       0, 11000, 20000, 32000, 47000, 51000, 71000
+};
+
+
+/* outputs atmospheric pressure associated with the given altitude. altitudes
+   are measured with respect to the mean sea level */
+real altitude_to_pressure(real altitude) {
+
+   real base_temperature = LAYER0_BASE_TEMPERATURE;
+   real base_pressure = LAYER0_BASE_PRESSURE;
+
+   real pressure;
+   real base; /* base for function to determine pressure */
+   real exponent; /* exponent for function to determine pressure */
+   int layer_number; /* identifies layer in the atmosphere */
+   int delta_z; /* difference between two altitudes */
+
+   if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
+      return 0;
+
+   /* calculate the base temperature and pressure for the atmospheric layer
+      associated with the inputted altitude */
+   for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
+      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+      if (lapse_rate[layer_number] == 0.0) {
+         exponent = GRAVITATIONAL_ACCELERATION * delta_z
+              / AIR_GAS_CONSTANT / base_temperature;
+         base_pressure *= exp(exponent);
+      }
+      else {
+         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+         exponent = GRAVITATIONAL_ACCELERATION /
+              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+         base_pressure *= pow(base, exponent);
+      }
+      base_temperature += delta_z * lapse_rate[layer_number];
+   }
+
+   /* calculate the pressure at the inputted altitude */
+   delta_z = altitude - base_altitude[layer_number];
+   if (lapse_rate[layer_number] == 0.0) {
+      exponent = GRAVITATIONAL_ACCELERATION * delta_z
+           / AIR_GAS_CONSTANT / base_temperature;
+      pressure = base_pressure * exp(exponent);
+   }
+   else {
+      base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+      exponent = GRAVITATIONAL_ACCELERATION /
+           (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+      pressure = base_pressure * pow(base, exponent);
+   }
+
+   return pressure;
+}
+
+
+/* outputs the altitude associated with the given pressure. the altitude
+   returned is measured with respect to the mean sea level */
+real pressure_to_altitude(real pressure) {
+
+   real next_base_temperature = LAYER0_BASE_TEMPERATURE;
+   real next_base_pressure = LAYER0_BASE_PRESSURE;
+
+   real altitude;
+   real base_pressure;
+   real base_temperature;
+   real base; /* base for function to determine base pressure of next layer */
+   real exponent; /* exponent for function to determine base pressure
+                             of next layer */
+   real coefficient;
+   int layer_number; /* identifies layer in the atmosphere */
+   int delta_z; /* difference between two altitudes */
+
+   if (pressure < 0)  /* illegal pressure */
+      return -1;
+   if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
+      return MAXIMUM_ALTITUDE;
+
+   /* calculate the base temperature and pressure for the atmospheric layer
+      associated with the inputted pressure. */
+   layer_number = -1;
+   do {
+      layer_number++;
+      base_pressure = next_base_pressure;
+      base_temperature = next_base_temperature;
+      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+      if (lapse_rate[layer_number] == 0.0) {
+         exponent = GRAVITATIONAL_ACCELERATION * delta_z
+              / AIR_GAS_CONSTANT / base_temperature;
+         next_base_pressure *= exp(exponent);
+      }
+      else {
+         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+         exponent = GRAVITATIONAL_ACCELERATION /
+              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+         next_base_pressure *= pow(base, exponent);
+      }
+      next_base_temperature += delta_z * lapse_rate[layer_number];
+   }
+   while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
+
+   /* calculate the altitude associated with the inputted pressure */
+   if (lapse_rate[layer_number] == 0.0) {
+      coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
+                                                    * base_temperature;
+      altitude = base_altitude[layer_number]
+                    + coefficient * log(pressure / base_pressure);
+   }
+   else {
+      base = pressure / base_pressure;
+      exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
+                                       / GRAVITATIONAL_ACCELERATION;
+      coefficient = base_temperature / lapse_rate[layer_number];
+      altitude = base_altitude[layer_number]
+                      + coefficient * (pow(base, exponent) - 1);
+   }
+
+   return altitude;
+}
+
+real feet_to_meters(real feet)
+{
+    return feet * (12 * 2.54 / 100);
+}
+
+real meters_to_feet(real meters)
+{
+    return meters / (12 * 2.54 / 100);
+}
+
+/*
+ * Values for our MP3H6115A pressure sensor
+ *
+ * From the data sheet:
+ *
+ * Pressure range: 15-115 kPa
+ * Voltage at 115kPa: 2.82
+ * Output scale: 27mV/kPa
+ *
+ *
+ * 27 mV/kPa * 2047 / 3300 counts/mV = 16.75 counts/kPa
+ * 2.82V * 2047 / 3.3 counts/V = 1749 counts/115 kPa
+ */
+
+real counts_per_kPa = 27 * 2047 / 3300;
+real counts_at_101_3kPa = 1674;
+
+real fraction_to_kPa(real fraction)
+{
+       return (fraction + 0.095) / 0.009;
+}
+
+
+real count_to_kPa(real count) = fraction_to_kPa(count / 2047);
+
+typedef struct {
+       real m, b;
+       int m_i, b_i;
+} line_t;
+
+line_t best_fit(real[] values, int first, int last) {
+       real sum_x = 0, sum_x2 = 0, sum_y = 0, sum_xy = 0;
+       int n = last - first + 1;
+       real m, b;
+       int m_i, b_i;
+
+       for (int i = first; i <= last; i++) {
+              sum_x += i;
+              sum_x2 += i**2;
+              sum_y += values[i];
+              sum_xy += values[i] * i;
+       }
+       m = (n*sum_xy - sum_y*sum_x) / (n*sum_x2 - sum_x**2);
+       b = sum_y/n - m*(sum_x/n);
+       return (line_t) { m = m, b = b };
+}
+
+real count_to_altitude(real count) {
+     return pressure_to_altitude(count_to_kPa(count) * 1000);
+}
+
+real fraction_to_altitude(real frac) = pressure_to_altitude(fraction_to_kPa(frac) * 1000);
+
+int num_samples = 1024;
+
+real[num_samples] alt = { [n] = fraction_to_altitude(n/(num_samples - 1)) };
+
+int num_part = 128;
+int seg_len = num_samples / num_part;
+
+line_t [dim(alt) / seg_len] fit = {
+       [n] = best_fit(alt, n * seg_len, n * seg_len + seg_len - 1)
+};
+
+int[num_samples/seg_len + 1]   alt_part;
+
+alt_part[0] = floor (fit[0].b + 0.5);
+alt_part[dim(fit)] = floor(fit[dim(fit)-1].m * dim(fit) * seg_len + fit[dim(fit)-1].b + 0.5);
+
+for (int i = 0; i < dim(fit) - 1; i++) {
+       real    here, there;
+       here = fit[i].m * (i+1) * seg_len + fit[i].b;
+       there = fit[i+1].m * (i+1) * seg_len + fit[i+1].b;
+       alt_part[i+1] = floor ((here + there) / 2 + 0.5);
+}
+
+real count_to_fit_altitude(int count) {
+       int     sub = count // seg_len;
+       int     off = count % seg_len;
+       line_t  l = fit[sub];
+       real r_v;
+       real i_v;
+
+       r_v = count * l.m + l.b;
+       i_v = (alt_part[sub] * (seg_len - off) + alt_part[sub+1] * off) / seg_len;
+       return i_v;
+}
+
+real max_error = 0;
+int max_error_count = 0;
+real total_error = 0;
+
+for (int count = 0; count < num_samples; count++) {
+       real    kPa = fraction_to_kPa(count / (num_samples - 1));
+       real    meters = pressure_to_altitude(kPa * 1000);
+
+       real    meters_approx = count_to_fit_altitude(count);
+       real    error = abs(meters - meters_approx);
+
+       total_error += error;
+       if (error > max_error) {
+               max_error = error;
+               max_error_count = count;
+       }
+#      printf ("       %7d,    /* %6.2g kPa %5d count approx %d */\n",
+#              floor (meters + 0.5), kPa, count, floor(count_to_fit_altitude(count) + 0.5));
+}
+
+printf ("/*max error %f at %7.3f%%. Average error %f*/\n", max_error, max_error_count / (num_samples - 1) * 100, total_error / num_samples);
+
+printf ("#define NALT %d\n", dim(alt_part));
+printf ("#define ALT_FRAC_BITS %d\n", floor (log2(32768/(dim(alt_part)-1)) + 0.1));
+
+for (int i = 0; i < dim(alt_part); i++) {
+       real fraction = i / (dim(alt_part) - 1);
+       real kPa = fraction_to_kPa(fraction);
+       printf ("%9d, /* %6.2f kPa %7.3f%% */\n",
+               alt_part[i], kPa, fraction * 100);
+}
diff --git a/src/util/make-altitude-pa b/src/util/make-altitude-pa
new file mode 100644 (file)
index 0000000..190b36f
--- /dev/null
@@ -0,0 +1,275 @@
+#!/usr/bin/nickle -f
+/*
+ * Pressure Sensor Model, version 1.1
+ *
+ * written by Holly Grimes
+ *
+ * Uses the International Standard Atmosphere as described in
+ *   "A Quick Derivation relating altitude to air pressure" (version 1.03)
+ *    from the Portland State Aerospace Society, except that the atmosphere
+ *    is divided into layers with each layer having a different lapse rate.
+ *
+ * Lapse rate data for each layer was obtained from Wikipedia on Sept. 1, 2007
+ *    at site <http://en.wikipedia.org/wiki/International_Standard_Atmosphere
+ *
+ * Height measurements use the local tangent plane.  The postive z-direction is up.
+ *
+ * All measurements are given in SI units (Kelvin, Pascal, meter, meters/second^2).
+ *   The lapse rate is given in Kelvin/meter, the gas constant for air is given
+ *   in Joules/(kilogram-Kelvin).
+ */
+
+const real GRAVITATIONAL_ACCELERATION = -9.80665;
+const real AIR_GAS_CONSTANT = 287.053;
+const int NUMBER_OF_LAYERS = 7;
+const real MAXIMUM_ALTITUDE = 84852;
+const real MINIMUM_PRESSURE = 0.3734;
+const real LAYER0_BASE_TEMPERATURE = 288.15;
+const real LAYER0_BASE_PRESSURE = 101325;
+
+/* lapse rate and base altitude for each layer in the atmosphere */
+const real[NUMBER_OF_LAYERS] lapse_rate = {
+       -0.0065, 0.0, 0.001, 0.0028, 0.0, -0.0028, -0.002
+};
+const int[NUMBER_OF_LAYERS] base_altitude = {
+       0, 11000, 20000, 32000, 47000, 51000, 71000
+};
+
+
+/* outputs atmospheric pressure associated with the given altitude. altitudes
+   are measured with respect to the mean sea level */
+real altitude_to_pressure(real altitude) {
+
+   real base_temperature = LAYER0_BASE_TEMPERATURE;
+   real base_pressure = LAYER0_BASE_PRESSURE;
+
+   real pressure;
+   real base; /* base for function to determine pressure */
+   real exponent; /* exponent for function to determine pressure */
+   int layer_number; /* identifies layer in the atmosphere */
+   int delta_z; /* difference between two altitudes */
+
+   if (altitude > MAXIMUM_ALTITUDE) /* FIX ME: use sensor data to improve model */
+      return 0;
+
+   /* calculate the base temperature and pressure for the atmospheric layer
+      associated with the inputted altitude */
+   for(layer_number = 0; layer_number < NUMBER_OF_LAYERS - 1 && altitude > base_altitude[layer_number + 1]; layer_number++) {
+      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+      if (lapse_rate[layer_number] == 0.0) {
+         exponent = GRAVITATIONAL_ACCELERATION * delta_z
+              / AIR_GAS_CONSTANT / base_temperature;
+         base_pressure *= exp(exponent);
+      }
+      else {
+         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+         exponent = GRAVITATIONAL_ACCELERATION /
+              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+         base_pressure *= pow(base, exponent);
+      }
+      base_temperature += delta_z * lapse_rate[layer_number];
+   }
+
+   /* calculate the pressure at the inputted altitude */
+   delta_z = altitude - base_altitude[layer_number];
+   if (lapse_rate[layer_number] == 0.0) {
+      exponent = GRAVITATIONAL_ACCELERATION * delta_z
+           / AIR_GAS_CONSTANT / base_temperature;
+      pressure = base_pressure * exp(exponent);
+   }
+   else {
+      base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+      exponent = GRAVITATIONAL_ACCELERATION /
+           (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+      pressure = base_pressure * pow(base, exponent);
+   }
+
+   return pressure;
+}
+
+
+/* outputs the altitude associated with the given pressure. the altitude
+   returned is measured with respect to the mean sea level */
+real pressure_to_altitude(real pressure) {
+
+   real next_base_temperature = LAYER0_BASE_TEMPERATURE;
+   real next_base_pressure = LAYER0_BASE_PRESSURE;
+
+   real altitude;
+   real base_pressure;
+   real base_temperature;
+   real base; /* base for function to determine base pressure of next layer */
+   real exponent; /* exponent for function to determine base pressure
+                             of next layer */
+   real coefficient;
+   int layer_number; /* identifies layer in the atmosphere */
+   int delta_z; /* difference between two altitudes */
+
+   if (pressure < 0)  /* illegal pressure */
+      return -1;
+   if (pressure < MINIMUM_PRESSURE) /* FIX ME: use sensor data to improve model */
+      return MAXIMUM_ALTITUDE;
+
+   /* calculate the base temperature and pressure for the atmospheric layer
+      associated with the inputted pressure. */
+   layer_number = -1;
+   do {
+      layer_number++;
+      base_pressure = next_base_pressure;
+      base_temperature = next_base_temperature;
+      delta_z = base_altitude[layer_number + 1] - base_altitude[layer_number];
+      if (lapse_rate[layer_number] == 0.0) {
+         exponent = GRAVITATIONAL_ACCELERATION * delta_z
+              / AIR_GAS_CONSTANT / base_temperature;
+         next_base_pressure *= exp(exponent);
+      }
+      else {
+         base = (lapse_rate[layer_number] * delta_z / base_temperature) + 1.0;
+         exponent = GRAVITATIONAL_ACCELERATION /
+              (AIR_GAS_CONSTANT * lapse_rate[layer_number]);
+         next_base_pressure *= pow(base, exponent);
+      }
+      next_base_temperature += delta_z * lapse_rate[layer_number];
+   }
+   while(layer_number < NUMBER_OF_LAYERS - 1 && pressure < next_base_pressure);
+
+   /* calculate the altitude associated with the inputted pressure */
+   if (lapse_rate[layer_number] == 0.0) {
+      coefficient = (AIR_GAS_CONSTANT / GRAVITATIONAL_ACCELERATION)
+                                                    * base_temperature;
+      altitude = base_altitude[layer_number]
+                    + coefficient * log(pressure / base_pressure);
+   }
+   else {
+      base = pressure / base_pressure;
+      exponent = AIR_GAS_CONSTANT * lapse_rate[layer_number]
+                                       / GRAVITATIONAL_ACCELERATION;
+      coefficient = base_temperature / lapse_rate[layer_number];
+      altitude = base_altitude[layer_number]
+                      + coefficient * (pow(base, exponent) - 1);
+   }
+
+   return altitude;
+}
+
+real feet_to_meters(real feet)
+{
+    return feet * (12 * 2.54 / 100);
+}
+
+real meters_to_feet(real meters)
+{
+    return meters / (12 * 2.54 / 100);
+}
+
+/*
+ * Values for our MS5607
+ *
+ * From the data sheet:
+ *
+ * Pressure range: 10-1200 mbar (1000 - 120000 Pa)
+ *
+ * Pressure data is reported in units of Pa
+ */
+
+typedef struct {
+       real m, b;
+       int m_i, b_i;
+} line_t;
+
+line_t best_fit(real[] values, int first, int last) {
+       real sum_x = 0, sum_x2 = 0, sum_y = 0, sum_xy = 0;
+       int n = last - first + 1;
+       real m, b;
+       int m_i, b_i;
+
+       for (int i = first; i <= last; i++) {
+              sum_x += i;
+              sum_x2 += i**2;
+              sum_y += values[i];
+              sum_xy += values[i] * i;
+       }
+       m = (n*sum_xy - sum_y*sum_x) / (n*sum_x2 - sum_x**2);
+       b = sum_y/n - m*(sum_x/n);
+       return (line_t) { m = m, b = b };
+}
+
+real   min_Pa = 0;
+real   max_Pa = 120000;
+
+/* Target is an array of < 2000 entries */
+int pa_sample_shift = 3;
+int pa_part_shift = 3;
+
+int num_part = ceil(max_Pa / (2 ** (pa_part_shift + pa_sample_shift)));
+
+int num_samples = num_part << pa_part_shift;
+
+real sample_to_Pa(int sample) = sample << pa_sample_shift;
+
+real sample_to_altitude(int sample) = pressure_to_altitude(sample_to_Pa(sample));
+
+int part_to_sample(int part) = part << pa_part_shift;
+
+real[num_samples] alt = { [n] = sample_to_altitude(n) };
+
+int seg_len = 1 << pa_part_shift;
+
+line_t [num_part] fit = {
+       [n] = best_fit(alt, n * seg_len, n * seg_len + seg_len - 1)
+};
+
+int[num_samples/seg_len + 1]   alt_part;
+
+alt_part[0] = floor (fit[0].b + 0.5);
+alt_part[dim(fit)] = floor(fit[dim(fit)-1].m * dim(fit) * seg_len + fit[dim(fit)-1].b + 0.5);
+
+for (int i = 0; i < dim(fit) - 1; i++) {
+       real    here, there;
+       here = fit[i].m * (i+1) * seg_len + fit[i].b;
+       there = fit[i+1].m * (i+1) * seg_len + fit[i+1].b;
+       alt_part[i+1] = floor ((here + there) / 2 + 0.5);
+}
+
+real sample_to_fit_altitude(int sample) {
+       int     sub = sample // seg_len;
+       int     off = sample % seg_len;
+       line_t  l = fit[sub];
+       real r_v;
+       real i_v;
+
+       r_v = sample * l.m + l.b;
+       i_v = (alt_part[sub] * (seg_len - off) + alt_part[sub+1] * off) / seg_len;
+       return i_v;
+}
+
+real max_error = 0;
+int max_error_sample = 0;
+real total_error = 0;
+
+for (int sample = 0; sample < num_samples; sample++) {
+       real    Pa = sample_to_Pa(sample);
+       real    meters = pressure_to_altitude(Pa);
+
+       real    meters_approx = sample_to_fit_altitude(sample);
+       real    error = abs(meters - meters_approx);
+
+       total_error += error;
+       if (error > max_error) {
+               max_error = error;
+               max_error_sample = sample;
+       }
+#      printf ("       %7d,    /* %6.2f kPa %5d sample approx %d */\n",
+#              floor (meters + 0.5), Pa / 1000, sample, floor(sample_to_fit_altitude(sample) + 0.5));
+}
+
+printf ("/*max error %f at %7.3f%%. Average error %f*/\n", max_error, max_error_sample / (num_samples - 1) * 100, total_error / num_samples);
+
+printf ("#define NALT %d\n", dim(alt_part));
+printf ("#define ALT_SHIFT %d\n", pa_part_shift + pa_sample_shift);
+
+for (int part = 0; part < dim(alt_part); part++) {
+       real kPa = sample_to_Pa(part_to_sample(part)) / 1000;
+       printf ("%9d, /* %6.2f kPa */\n",
+               alt_part[part], kPa);
+}
diff --git a/src/util/make-kalman b/src/util/make-kalman
new file mode 100644 (file)
index 0000000..fd33bab
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+cd $1 2> /dev/null 1>&2
+
+SIGMA_BOTH="-M 2 -H 6 -A 2"
+SIGMA_BARO="-M 2 -H 6 -A 2"
+SIGMA_ACCEL="-M 2 -H 4 -A 4"
+SIGMA_BOTH_NOISY_ACCEL="-M 2 -H 6 -A 3"
+
+echo '#if NOISY_ACCEL'
+echo
+echo '/* TeleMetrum v1.0 boards have noisy accelerometer values'
+echo ' * increase the sigma value for accel data to compensate.'
+echo ' * This improves the accuracy of apogee detection.'
+echo ' */'
+echo
+
+nickle kalman.5c -p AO_BOTH -c both -t 0.01 $SIGMA_BOTH_NOISY_ACCEL
+nickle kalman.5c -p AO_BOTH -c both -t 0.1 $SIGMA_BOTH_NOISY_ACCEL
+nickle kalman.5c -p AO_BOTH -c both -t 1 $SIGMA_BOTH_NOISY_ACCEL
+
+echo '#endif'
+echo
+echo '#ifndef AO_BOTH_K00_100'
+echo
+
+nickle kalman.5c -p AO_BOTH -c both -t 0.01 $SIGMA_BOTH
+nickle kalman.5c -p AO_BOTH -c both -t 0.1 $SIGMA_BOTH
+nickle kalman.5c -p AO_BOTH -c both -t 1 $SIGMA_BOTH
+
+echo '#endif'
+echo
+
+nickle kalman.5c -p AO_ACCEL -c accel -t 0.01 $SIGMA_ACCEL
+nickle kalman.5c -p AO_ACCEL -c accel -t 0.1 $SIGMA_ACCEL
+nickle kalman.5c -p AO_ACCEL -c accel -t 1 $SIGMA_ACCEL
+
+nickle kalman.5c -p AO_BARO -c baro -t 0.01 $SIGMA_BARO
+nickle kalman.5c -p AO_BARO -c baro -t 0.1 $SIGMA_BARO
+nickle kalman.5c -p AO_BARO -c baro -t 1 $SIGMA_BARO
+
diff --git a/src/util/make-whiten b/src/util/make-whiten
new file mode 100644 (file)
index 0000000..d68a02a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2012 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.
+ */
+
+/* Generate the data whitening array used by the CC11* radios */
+
+int pn9 = 0x1ff;
+
+void
+pn9_step() {
+       pn9 = (pn9 >> 1) | (((pn9 ^ (pn9 >> 5)) & 1) << 8);
+}
+
+int val()
+{
+       int     ret = pn9 & 0xff;
+
+       for (int i = 0; i < 8; i++)
+               pn9_step();
+       return ret;
+}
+
+for (int i = 0; i < 128; i++)
+       printf (" /* %3d */ 0x%02x,\n", i + 1, val());
diff --git a/src/util/sirf-cksum b/src/util/sirf-cksum
new file mode 100755 (executable)
index 0000000..b905f31
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/env nickle
+
+int checksum(int[] msg)
+{
+       int sum = 0;
+       for (int i = 0; i < dim(msg); i++) {
+               sum += msg[i];
+               sum &= 0x7fff;
+       }
+       return sum;
+}
+
+void main()
+{
+       string[...]     input;
+       int[...]        msg;
+
+       setdim(input, 0);
+       while (!File::end(stdin)) {
+               input[dim(input)] = gets();
+       }
+
+       setdim(msg, 0);
+       for (int i = 0; i < dim(input); i++) {
+               string[*] words = String::wordsplit(input[i], " ,\t");
+               for (int j = 0; j < dim(words); j++) {
+                       if (words[j] == "/" + "*")
+                               break;
+                       if (String::length(words[j]) > 0 &&
+                           Ctype::isdigit(words[j][0])) {
+                               msg[dim(msg)] = string_to_integer(words[j]);
+                       }
+                }
+       }
+       printf("\t0xa0, 0xa2, 0x%02x, 0x%02x,\t/* length: %d bytes */\n",
+              dim(msg) >> 8, dim(msg) & 0xff, dim(msg));
+       for (int i = 0; i < dim(input); i++)
+               printf("%s\n", input[i]);
+       int csum = checksum(msg);
+       printf ("\t0x%02x, 0x%02x, 0xb0, 0xb3,\n",
+               csum >> 8, csum & 0xff);
+}
+
+main();
diff --git a/src/util/skytraq-cksum b/src/util/skytraq-cksum
new file mode 100644 (file)
index 0000000..ab0464a
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/env nickle
+
+int checksum(int[] msg)
+{
+       int sum = 0;
+       for (int i = 0; i < dim(msg); i++) {
+               sum ^= msg[i];
+               sum &= 0xff;
+       }
+       return sum;
+}
+
+void main()
+{
+       string[...]     input;
+       int[...]        msg;
+
+       setdim(input, 0);
+       while (!File::end(stdin)) {
+               input[dim(input)] = gets();
+       }
+
+       setdim(msg, 0);
+       for (int i = 0; i < dim(input); i++) {
+               string[*] words = String::wordsplit(input[i], " ,\t");
+               for (int j = 0; j < dim(words); j++) {
+                       if (words[j] == "/" + "*")
+                               break;
+                       if (String::length(words[j]) > 0 &&
+                           Ctype::isdigit(words[j][0])) {
+                               msg[dim(msg)] = string_to_integer(words[j]);
+                       }
+                }
+       }
+       printf("\t0xa0, 0xa1, 0x%02x, 0x%02x,\t\t/* length: %d bytes */\n",
+              dim(msg) >> 8, dim(msg) & 0xff, dim(msg));
+       for (int i = 0; i < dim(input); i++)
+               printf("%s\n", input[i]);
+       int csum = checksum(msg);
+       printf ("\t0x%02x, 0x0d, 0x0a,\n",
+               csum);
+}
+
+main();
diff --git a/telemetrum.inf b/telemetrum.inf
new file mode 100755 (executable)
index 0000000..b4a84b9
--- /dev/null
@@ -0,0 +1,88 @@
+; Copyright (C) 2010 Keith Packard (keithp@keithp.com)\r
+; released under GNU General Public License version 2\r
+\r
+[Version]\r
+Signature      = "$Windows NT$"\r
+Class          = Modem\r
+ClassGUID      = {4D36E96D-E325-11CE-BFC1-08002BE10318}\r
+Provider       = %Mfg%\r
+DriverVer      = 08/05/2010,7.1.1.0\r
+PnpLockDown    = 0\r
+DriverPackageDisplayName = %DriverName%\r
+\r
+[DestinationDirs]\r
+FakeModemCopyFileSection = 12\r
+DefaultDestDir = 12\r
+\r
+[ControlFlags]\r
+\r
+[Manufacturer]\r
+%Mfg% = Models, NTx86, NTamd64, NTia64\r
+\r
+[Models]\r
+%AltusMetrum%  = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
+%TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
+%TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
+%TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+\r
+[Models.NTx86]\r
+%AltusMetrum%  = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
+%TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
+%TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
+%TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+\r
+[Models.NTamd64]\r
+%AltusMetrum%  = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
+%TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
+%TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
+%TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+\r
+[Models.NTia64]\r
+%AltusMetrum%  = AltusMetrum.Install, USB\VID_FFFE&PID_000A, AltusMetrumSerial\r
+%TeleMetrum%   = AltusMetrum.Install, USB\VID_FFFE&PID_000B, AltusMetrumSerial\r
+%TeleDongle%   = AltusMetrum.Install, USB\VID_FFFE&PID_000C, AltusMetrumSerial\r
+%TeleTerra%    = AltusMetrum.Install, USB\VID_FFFE&PID_000D, AltusMetrumSerial\r
+\r
+;----------------------------------------------------------------------------\r
+; Installation sections\r
+;----------------------------------------------------------------------------\r
+\r
+[AltusMetrum.Install.NT]\r
+include                = mdmcpq.inf\r
+CopyFiles      = FakeModemCopyFileSection\r
+AddReg         = All.AddReg, Modem.AddReg, Uninstall.AddReg\r
+\r
+[AltusMetrum.Install.NT.Services]\r
+include                = mdmcpq.inf\r
+AddService     = usbser, 0x00000000, LowerFilter_Service_Inst\r
+\r
+[AltusMetrum.Install.NT.HW]\r
+include                = mdmcpq.inf\r
+AddReg         = LowerFilterAddReg\r
+\r
+;----------------------------------------------------------------------------\r
+; AddReg sections\r
+;----------------------------------------------------------------------------\r
+\r
+[All.AddReg]\r
+HKR,,FriendlyDriver,,          Unimodem.vxd\r
+HKR,,DevLoader,,               *vcomm\r
+HKR,,ConfigDialog,,            modemui.dll\r
+HKR,,EnumPropPages,,           "modemui.dll,EnumPropPages"\r
+HKR,,PortSubClass, 1,          02\r
+HKR,,DeviceType, 1,            01\r
+\r
+[Modem.AddReg]\r
+HKR,, Properties, 1, C0,01,00,00, 00,00,00,00, FF,00,00,00, 07,00,00,00, 0F,00,00,00, F7,0F,00,00, 00,84,03,00, C0,DA,00,00\r
+\r
+[Uninstall.AddReg]\r
+HKLM,Software\Microsoft\Windows\CurrentVersion\Uninstall\%TeleMetrum%,DisplayName,,"%TeleMetrum%"\r
+\r
+[Strings]\r
+Mfg            = "altusmetrum.org"\r
+AltusMetrum    = "AltusMetrum"\r
+TeleMetrum     = "TeleMetrum"\r
+TeleDongle     = "TeleDongle"\r
+TeleTerra      = "TeleTerra"\r
+DriverName     = "Altus Metrum Device Driver"\r
+\r
diff --git a/themes/background.png b/themes/background.png
new file mode 100644 (file)
index 0000000..a3c78f3
Binary files /dev/null and b/themes/background.png differ
diff --git a/themes/background.xcf b/themes/background.xcf
new file mode 100644 (file)
index 0000000..92c1709
Binary files /dev/null and b/themes/background.xcf differ
diff --git a/themes/gdm/GdmGreeterTheme.desktop b/themes/gdm/GdmGreeterTheme.desktop
new file mode 100644 (file)
index 0000000..b0fa34b
--- /dev/null
@@ -0,0 +1,7 @@
+[GdmGreeterTheme]
+Greeter=altusmetrum.xml
+Name=AltusMetrum
+Description=altusmetrum.org
+Author=Bdale Garbee
+Copyright=Bdale Garbee
+Screenshot=screenshot.png
diff --git a/themes/gdm/altusmetrum.xml b/themes/gdm/altusmetrum.xml
new file mode 100644 (file)
index 0000000..e2755e8
--- /dev/null
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<!DOCTYPE greeter SYSTEM "greeter.dtd">
+<greeter>
+
+<item type="pixmap">
+       <normal file="background.png"/>
+       <pos x="0" y="0" width="100%" height="100%"/>
+</item>
+
+<item type="rect">
+       <normal color="#ffffff" alpha="0.1"/>
+       <pos anchor="center" x="50%" y="94%" width="100%" height="6%"/>
+       <fixed>
+               <item type="rect">
+                       <normal color="#000000" alpha="0.3"/>
+                       <pos anchor="w" x="84%" y="50%" height="24" width="15%"/>
+                       <fixed>
+                               <item type="entry" id="user-pw-entry">
+                                       <pos anchor="nw" x="1" y="1" height="-2" width="-2"/>
+                                       <normal color="#000000" font="Bitstream Vera Sans 10"/>
+                               </item>
+                       </fixed>
+               </item>
+       </fixed>
+</item>
+
+<item type="label" id="system_button" button="true">
+       <pos anchor="w" x="1%" y="94%"/>
+       <normal font="Bitstream Vera Sans Bold 10" color="#ffffff" alpha="0.8"/>
+       <active font="Bitstream Vera Sans Bold 10" color="#ffffff" alpha="0.8"/>
+       <prelight font="Bitstream Vera Sans Bold 10" color="#ffffff"/>
+       <text>%n | %c</text>
+</item>
+
+<item type="label" id="pam-prompt">
+       <pos anchor="e" x="83%" y="94%"/>
+       <normal font="Bitstream Vera Sans Bold 10" color="#ffffff" alpha="0.8"/>
+       <stock type="username-label"/>
+</item>
+
+<item type="label" id="timed-label">
+       <pos anchor="center" x="50%" y="30%"/>
+       <show type="timed"/>
+       <normal font="Bitstream Vera Sans Bold 10" color="#ffffff"/>
+       <stock type="timed-label"/>
+</item>
+
+<item type="label" id="pam-error">
+       <pos anchor="center" x="50%" y="50%"/>
+       <normal font="Bitstream Vera Sans Bold 10" color="#ffffff"/>
+       <text></text>
+</item>
+
+<item type="label" id="caps-lock-warning">
+       <pos anchor="center" x="50%" y="70%"/>
+       <normal font="Bitstream Vera Sans Bold 10" color="#ffffff"/>
+       <stock type="caps-lock-warning"/>
+</item>
+
+</greeter>
diff --git a/themes/gdm/screenshot.png b/themes/gdm/screenshot.png
new file mode 100644 (file)
index 0000000..5c49ec1
Binary files /dev/null and b/themes/gdm/screenshot.png differ
diff --git a/themes/slim/panel.png b/themes/slim/panel.png
new file mode 100644 (file)
index 0000000..f61bafe
Binary files /dev/null and b/themes/slim/panel.png differ
diff --git a/themes/slim/slim.theme b/themes/slim/slim.theme
new file mode 100644 (file)
index 0000000..1eeef96
--- /dev/null
@@ -0,0 +1,37 @@
+# altusmetrum theme for SLiM
+# by Bdale Garbee <bdale@gag.com>
+
+# Messages (ie: shutdown)
+msg_color               #000000
+msg_font                Verdana:size=18:bold:dpi=75
+msg_x                   50%
+msg_y                   40%
+msg_shadow_color #FFFFFF
+msg_shadow_xoffset 1
+msg_shadow_yoffset 1
+
+# valid values: stretch, tile
+background_style        stretch
+background_color        #eedddd
+
+# Input controls
+input_panel_x           50%
+input_panel_y           70%
+input_name_x            59
+input_name_y            161
+input_font             Verdana:size=12:dpi=75
+input_fgcolor           #000000
+
+# Username / password request
+username_font                  Verdana:size=14:bold:dpi=75
+username_color         #000000
+username_x              50%
+username_y              122
+password_x              50%
+password_y              122
+username_shadow_color   #FFFFFF
+username_shadow_xoffset 1
+username_shadow_yoffset 1
+
+username_msg            Username:
+password_msg            Password: