Merge branch 'branch-1.6' into debian
authorBdale Garbee <bdale@gag.com>
Thu, 16 Jul 2015 19:36:59 +0000 (13:36 -0600)
committerBdale Garbee <bdale@gag.com>
Thu, 16 Jul 2015 19:36:59 +0000 (13:36 -0600)
425 files changed:
ChangeLog
altosdroid/AndroidManifest.xml [deleted file]
altosdroid/AndroidManifest.xml.in [new file with mode: 0644]
altosdroid/Makefile.am
altosdroid/Notebook
altosdroid/default.properties
altosdroid/project.properties
altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png [new file with mode: 0644]
altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png [new file with mode: 0644]
altosdroid/res/layout/device_list.xml
altosdroid/res/layout/map_preload.xml [new file with mode: 0644]
altosdroid/res/layout/map_type.xml [new file with mode: 0644]
altosdroid/res/layout/tab_ascent.xml [deleted file]
altosdroid/res/layout/tab_descent.xml [deleted file]
altosdroid/res/layout/tab_flight.xml [new file with mode: 0644]
altosdroid/res/layout/tab_landed.xml [deleted file]
altosdroid/res/layout/tab_layout.xml [new file with mode: 0644]
altosdroid/res/layout/tab_map.xml
altosdroid/res/layout/tab_pad.xml
altosdroid/res/layout/tab_recover.xml [new file with mode: 0644]
altosdroid/res/menu/option_menu.xml
altosdroid/res/values/Colors.xml [new file with mode: 0644]
altosdroid/res/values/CustomTheme.xml [new file with mode: 0644]
altosdroid/res/values/strings.xml
altosdroid/res/xml/device_filter.xml [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosBluetooth.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroid.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferences.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidPreferencesBackend.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidTab.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/AltosViewPager.java
altosdroid/src/org/altusmetrum/AltosDroid/AltosVoice.java
altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/DeviceListActivity.java
altosdroid/src/org/altusmetrum/AltosDroid/GoNoGoLights.java
altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java [deleted file]
altosdroid/src/org/altusmetrum/AltosDroid/TabMap.java
altosdroid/src/org/altusmetrum/AltosDroid/TabPad.java
altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java [new file with mode: 0644]
altosdroid/src/org/altusmetrum/AltosDroid/TabsAdapter.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryLogger.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryReader.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryService.java
altosdroid/src/org/altusmetrum/AltosDroid/TelemetryState.java
altoslib/.gitignore
altoslib/AltosAccel.java
altoslib/AltosCRCException.java
altoslib/AltosCSV.java
altoslib/AltosCompanion.java
altoslib/AltosConfigData.java
altoslib/AltosConfigDataException.java
altoslib/AltosConfigValues.java
altoslib/AltosConvert.java
altoslib/AltosDebug.java
altoslib/AltosDistance.java
altoslib/AltosEeprom.java
altoslib/AltosEepromChunk.java
altoslib/AltosEepromDownload.java
altoslib/AltosEepromFile.java
altoslib/AltosEepromGPS.java
altoslib/AltosEepromHeader.java
altoslib/AltosEepromIterable.java
altoslib/AltosEepromList.java
altoslib/AltosEepromLog.java
altoslib/AltosEepromMega.java
altoslib/AltosEepromMetrum2.java
altoslib/AltosEepromMini.java
altoslib/AltosEepromMonitor.java
altoslib/AltosEepromTM.java
altoslib/AltosEepromTm.java
altoslib/AltosFile.java
altoslib/AltosFlash.java
altoslib/AltosFlashListener.java
altoslib/AltosFlightDisplay.java [new file with mode: 0644]
altoslib/AltosFlightReader.java
altoslib/AltosFlightStats.java
altoslib/AltosFontListener.java [new file with mode: 0644]
altoslib/AltosFrequency.java
altoslib/AltosGPS.java
altoslib/AltosGPSSat.java
altoslib/AltosGreatCircle.java
altoslib/AltosHeight.java
altoslib/AltosHexfile.java
altoslib/AltosHexsym.java
altoslib/AltosIMU.java
altoslib/AltosIdle.java
altoslib/AltosIdleFetch.java
altoslib/AltosIdleMonitor.java
altoslib/AltosIdleMonitorListener.java
altoslib/AltosIgnite.java
altoslib/AltosImage.java [new file with mode: 0644]
altoslib/AltosKML.java
altoslib/AltosLatLon.java [new file with mode: 0644]
altoslib/AltosLatitude.java
altoslib/AltosLaunchSite.java [new file with mode: 0644]
altoslib/AltosLaunchSiteListener.java [new file with mode: 0644]
altoslib/AltosLaunchSites.java [new file with mode: 0644]
altoslib/AltosLib.java
altoslib/AltosLine.java
altoslib/AltosLink.java
altoslib/AltosListenerState.java
altoslib/AltosLocation.java
altoslib/AltosLog.java
altoslib/AltosLongitude.java
altoslib/AltosMag.java
altoslib/AltosMap.java [new file with mode: 0644]
altoslib/AltosMapCache.java [new file with mode: 0644]
altoslib/AltosMapCacheListener.java [new file with mode: 0644]
altoslib/AltosMapInterface.java [new file with mode: 0644]
altoslib/AltosMapLine.java [new file with mode: 0644]
altoslib/AltosMapLoader.java [new file with mode: 0644]
altoslib/AltosMapLoaderListener.java [new file with mode: 0644]
altoslib/AltosMapMark.java [new file with mode: 0644]
altoslib/AltosMapPath.java [new file with mode: 0644]
altoslib/AltosMapPathPoint.java [new file with mode: 0644]
altoslib/AltosMapRectangle.java [new file with mode: 0644]
altoslib/AltosMapStore.java [new file with mode: 0644]
altoslib/AltosMapStoreListener.java [new file with mode: 0644]
altoslib/AltosMapTile.java [new file with mode: 0644]
altoslib/AltosMapTileListener.java [new file with mode: 0644]
altoslib/AltosMapTransform.java [new file with mode: 0644]
altoslib/AltosMapZoomListener.java [new file with mode: 0644]
altoslib/AltosMma655x.java
altoslib/AltosMs5607.java
altoslib/AltosNoSymbol.java
altoslib/AltosOrient.java
altoslib/AltosParse.java
altoslib/AltosPointDouble.java [new file with mode: 0644]
altoslib/AltosPointInt.java [new file with mode: 0644]
altoslib/AltosPreferences.java
altoslib/AltosPreferencesBackend.java
altoslib/AltosProgrammer.java
altoslib/AltosPyro.java
altoslib/AltosQuaternion.java
altoslib/AltosRectangle.java [new file with mode: 0644]
altoslib/AltosReplayReader.java
altoslib/AltosRomconfig.java
altoslib/AltosRotation.java
altoslib/AltosSavedState.java
altoslib/AltosSelfFlash.java
altoslib/AltosSensorEMini.java
altoslib/AltosSensorMM.java
altoslib/AltosSensorMega.java
altoslib/AltosSensorMetrum.java
altoslib/AltosSensorTGPS.java
altoslib/AltosSensorTM.java
altoslib/AltosSensorTMini.java
altoslib/AltosSpeed.java
altoslib/AltosState.java
altoslib/AltosStateIterable.java
altoslib/AltosStateUpdate.java
altoslib/AltosTelemetry.java
altoslib/AltosTelemetryConfiguration.java
altoslib/AltosTelemetryFile.java
altoslib/AltosTelemetryIterable.java
altoslib/AltosTelemetryLegacy.java
altoslib/AltosTelemetryLocation.java
altoslib/AltosTelemetryMap.java
altoslib/AltosTelemetryMegaData.java
altoslib/AltosTelemetryMegaSensor.java
altoslib/AltosTelemetryMetrumData.java
altoslib/AltosTelemetryMetrumSensor.java
altoslib/AltosTelemetryMini.java
altoslib/AltosTelemetryRaw.java
altoslib/AltosTelemetryReader.java
altoslib/AltosTelemetrySatellite.java
altoslib/AltosTelemetrySensor.java
altoslib/AltosTelemetryStandard.java
altoslib/AltosTemperature.java
altoslib/AltosUnits.java
altoslib/AltosUnitsListener.java
altoslib/AltosVersion.java.in [new file with mode: 0644]
altoslib/AltosVoltage.java
altoslib/AltosWriter.java
altoslib/Makefile.am
altosui/Altos.java
altosui/AltosAscent.java
altosui/AltosCompanionInfo.java
altosui/AltosConfig.java
altosui/AltosConfigPyroUI.java
altosui/AltosConfigTD.java
altosui/AltosConfigTDUI.java
altosui/AltosConfigUI.java
altosui/AltosConfigureUI.java
altosui/AltosDescent.java
altosui/AltosFlightStatus.java
altosui/AltosFlightStatusTableModel.java
altosui/AltosFlightStatusUpdate.java
altosui/AltosFlightUI.java
altosui/AltosGraphUI.java
altosui/AltosIdleMonitorUI.java
altosui/AltosIgniteUI.java
altosui/AltosIgnitor.java
altosui/AltosLanded.java
altosui/AltosLaunch.java
altosui/AltosLaunchUI.java
altosui/AltosPad.java
altosui/AltosUI.java
altosui/altos-windows.nsi.in
altosuilib/.gitignore
altosuilib/AltosBTDevice.java
altosuilib/AltosBTDeviceIterator.java
altosuilib/AltosBTKnown.java
altosuilib/AltosBTManage.java
altosuilib/AltosCSVUI.java
altosuilib/AltosConfigFreqUI.java
altosuilib/AltosDataChooser.java
altosuilib/AltosDevice.java
altosuilib/AltosDeviceDialog.java
altosuilib/AltosDeviceUIDialog.java
altosuilib/AltosDisplayThread.java
altosuilib/AltosEepromDelete.java
altosuilib/AltosEepromManage.java
altosuilib/AltosEepromMonitor.java
altosuilib/AltosEepromMonitorUI.java
altosuilib/AltosEepromSelect.java
altosuilib/AltosFlashUI.java
altosuilib/AltosFlightDisplay.java [deleted file]
altosuilib/AltosFlightInfoTableModel.java
altosuilib/AltosFlightStatsTable.java
altosuilib/AltosFontListener.java [deleted file]
altosuilib/AltosGraph.java
altosuilib/AltosGraphDataPoint.java
altosuilib/AltosGraphDataSet.java
altosuilib/AltosInfoTable.java
altosuilib/AltosLed.java
altosuilib/AltosLights.java
altosuilib/AltosPositionListener.java
altosuilib/AltosRomconfigUI.java
altosuilib/AltosScanUI.java
altosuilib/AltosSerial.java
altosuilib/AltosSerialInUseException.java
altosuilib/AltosUIAxis.java
altosuilib/AltosUIConfigure.java
altosuilib/AltosUIDataMissing.java
altosuilib/AltosUIDataPoint.java
altosuilib/AltosUIDataSet.java
altosuilib/AltosUIDialog.java
altosuilib/AltosUIEnable.java
altosuilib/AltosUIFlightTab.java
altosuilib/AltosUIFrame.java
altosuilib/AltosUIFreqList.java
altosuilib/AltosUIGraph.java
altosuilib/AltosUIGrapher.java
altosuilib/AltosUIImage.java [new file with mode: 0644]
altosuilib/AltosUIIndicator.java
altosuilib/AltosUILatLon.java [deleted file]
altosuilib/AltosUILib.java
altosuilib/AltosUIListener.java
altosuilib/AltosUIMap.java [deleted file]
altosuilib/AltosUIMapCache.java [deleted file]
altosuilib/AltosUIMapCacheListener.java [deleted file]
altosuilib/AltosUIMapImage.java [deleted file]
altosuilib/AltosUIMapLine.java [deleted file]
altosuilib/AltosUIMapMark.java [deleted file]
altosuilib/AltosUIMapNew.java [new file with mode: 0644]
altosuilib/AltosUIMapPath.java [deleted file]
altosuilib/AltosUIMapPreload.java [deleted file]
altosuilib/AltosUIMapPreloadNew.java [new file with mode: 0644]
altosuilib/AltosUIMapRectangle.java [deleted file]
altosuilib/AltosUIMapStore.java [deleted file]
altosuilib/AltosUIMapStoreListener.java [deleted file]
altosuilib/AltosUIMapTile.java [deleted file]
altosuilib/AltosUIMapTileListener.java [deleted file]
altosuilib/AltosUIMapTransform.java [deleted file]
altosuilib/AltosUIMapView.java [deleted file]
altosuilib/AltosUIMapZoomListener.java [deleted file]
altosuilib/AltosUIMarker.java
altosuilib/AltosUIPreferences.java
altosuilib/AltosUIPreferencesBackend.java
altosuilib/AltosUIRateList.java
altosuilib/AltosUISeries.java
altosuilib/AltosUITelemetryList.java
altosuilib/AltosUIUnitsIndicator.java
altosuilib/AltosUIVersion.java.in [deleted file]
altosuilib/AltosUIVoltageIndicator.java
altosuilib/AltosUSBDevice.java
altosuilib/AltosVoice.java
altosuilib/GrabNDrag.java
altosuilib/Makefile.am
altosuilib/OSXAdapter.java
ao-bringup/cal-freq
ao-bringup/test-easymega
ao-bringup/test-telemetrum
ao-bringup/turnon_easymega
ao-bringup/turnon_telebt
ao-bringup/turnon_telegps
ao-bringup/turnon_telemini
ao-tools/Makefile.am
ao-tools/ao-cal-freq/.gitignore [new file with mode: 0644]
ao-tools/ao-cal-freq/Makefile.am [new file with mode: 0644]
ao-tools/ao-cal-freq/ao-cal-freq.1 [new file with mode: 0644]
ao-tools/ao-cal-freq/ao-cal-freq.c [new file with mode: 0644]
ao-tools/ao-dump-up/ao-dump-up.c
ao-tools/ao-flash/Makefile.am
ao-tools/ao-flash/ao-flash-stm [changed mode: 0644->0755]
ao-tools/ao-flash/ao-flash-stm32f0x [new file with mode: 0755]
ao-tools/ao-flash/ao-flash-stm32f0x.1 [new file with mode: 0644]
ao-tools/ao-list/ao-list.c
ao-tools/ao-mega/ao-mega.c
ao-tools/ao-usbtrng/ao-usbtrng.1
ao-tools/ao-usbtrng/ao-usbtrng.c
ao-tools/lib/cc-usb.c
ao-tools/lib/cc-usbdev.c
ao-tools/lib/cc.h
configure.ac
debian/control
doc/Makefile
doc/altusmetrum.xsl
doc/load-maps.png
doc/monitor-idle.png [new file with mode: 0644]
doc/release-notes-1.6.1.xsl [new file with mode: 0644]
doc/telemetry.xsl
icon/Makefile.am
icon/make-rc [new file with mode: 0755]
icon/windows-stub.c
micropeak/Makefile.am
micropeak/MicroData.java
micropeak/MicroDataPoint.java
micropeak/MicroDeviceDialog.java
micropeak/MicroDownload.java
micropeak/MicroExport.java
micropeak/MicroFile.java
micropeak/MicroFileChooser.java
micropeak/MicroFrame.java
micropeak/MicroGraph.java
micropeak/MicroPeak.java
micropeak/MicroRaw.java
micropeak/MicroSave.java
micropeak/MicroSerial.java
micropeak/MicroSerialLog.java
micropeak/MicroStats.java
micropeak/MicroStatsTable.java
micropeak/MicroUSB.java
micropeak/micropeak-windows.nsi.in
src/Makefile
src/cc1111/ao_arch.h
src/cc1111/ao_button.c
src/cc1111/ao_radio.c
src/chaoskey-v0.1/.gitignore [new file with mode: 0644]
src/chaoskey-v0.1/Makefile [new file with mode: 0644]
src/chaoskey-v0.1/ao_chaoskey.c [new file with mode: 0644]
src/chaoskey-v0.1/ao_pins.h [new file with mode: 0644]
src/chaoskey-v0.1/flash-loader/.gitignore [new file with mode: 0644]
src/chaoskey-v0.1/flash-loader/Makefile [new file with mode: 0644]
src/chaoskey-v0.1/flash-loader/ao_pins.h [new file with mode: 0644]
src/drivers/ao_aprs.c
src/drivers/ao_btm.c
src/drivers/ao_cc1120.c
src/drivers/ao_cc1200.c
src/drivers/ao_cc1200.h
src/drivers/ao_cc1200_CC1200.h
src/drivers/ao_companion.c
src/drivers/ao_hmc5883.c
src/drivers/ao_lco.c [new file with mode: 0644]
src/drivers/ao_lco.h [new file with mode: 0644]
src/drivers/ao_packet.c
src/drivers/ao_packet_master.c
src/drivers/ao_packet_slave.c
src/drivers/ao_pad.c
src/drivers/ao_pad.h
src/drivers/ao_trng.c [new file with mode: 0644]
src/drivers/ao_trng.h [new file with mode: 0644]
src/drivers/ao_trng_send.c [new file with mode: 0644]
src/drivers/ao_trng_send.h [new file with mode: 0644]
src/kernel/ao_config.c
src/kernel/ao_config.h
src/kernel/ao_packet.h
src/kernel/ao_product.c
src/kernel/ao_pyro.c
src/kernel/ao_radio_cmac.c
src/kernel/ao_serial.h
src/kernel/ao_task.c
src/kernel/ao_task.h
src/kernel/ao_telemetry.c
src/kernel/ao_telemetry.h
src/micropeak/micropeak-load.tmpl
src/microsplash/.gitignore
src/microsplash/Makefile
src/microsplash/microsplash-load.tmpl [new file with mode: 0644]
src/product/ao_terraui.c
src/stm/ao_i2c_stm.c
src/stm/ao_serial_stm.c
src/stmf0/ao_adc_fast.c
src/stmf0/ao_adc_fast.h
src/stmf0/ao_crc_stm.c [new file with mode: 0644]
src/stmf0/ao_exti.h [new file with mode: 0644]
src/stmf0/ao_usb_stm.c
src/telebt-v3.0/ao_pins.h
src/telelco-v0.1/ao_lco.c
src/telelco-v0.2/ao_lco.c [deleted file]
src/telelco-v0.2/ao_lco.h [deleted file]
src/telelco-v0.3/.gitignore [new file with mode: 0644]
src/telelco-v0.3/Makefile [new file with mode: 0644]
src/telelco-v0.3/ao_pins.h [new file with mode: 0644]
src/telelco-v0.3/ao_telelco.c [new file with mode: 0644]
src/telelco-v0.3/flash-loader/Makefile [new file with mode: 0644]
src/telelco-v0.3/flash-loader/ao_pins.h [new file with mode: 0644]
src/test/ao_flight_test.c
src/usbtrng-v2.0/Makefile
src/usbtrng-v2.0/ao_pins.h
src/usbtrng-v2.0/ao_usbtrng.c
src/util/ao-make-product.5c
telegps/TeleGPS.java
telegps/TeleGPSConfig.java
telegps/TeleGPSConfigUI.java
telegps/TeleGPSDisplayThread.java
telegps/TeleGPSGraphUI.java
telegps/TeleGPSInfo.java
telegps/TeleGPSPreferences.java
telegps/TeleGPSState.java
telegps/TeleGPSStatus.java
telegps/TeleGPSStatusUpdate.java
telegps/telegps-windows.nsi.in

index c4808abd6781f5a697024f4983cf636d80c2f58c..3a3c6b5ed41bdfa7b015e50a8101318b7015adaf 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
+commit 6e9bb9178356620bd47d9f2e31abf42b7f1a8f11
+Merge: e2cefd8 87c8bb3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 16 13:31:42 2015 -0600
+
+    Merge branch 'master' into branch-1.6
+
+commit 87c8bb3956897830da1f7aaca2990a9571767b73
+Merge: 643c2fb d6445b3
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Thu Jul 16 07:54:35 2015 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit d6445b3739ac2c5dd040efdb97317a6b2a48044a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 15 18:31:05 2015 -0700
+
+    Bump Java library versions
+    
+    Avoid problems if you have an old version of the library installed
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 643c2fb03833d658320f476ef731bbb06fe3cc31
+Merge: e41786f 271f56a
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Wed Jul 15 16:43:50 2015 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 271f56a41c7e785b0fab7e572325df842d104277
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 15 11:41:03 2015 -0700
+
+    Bump configure.ac versions to 1.6.1
+    
+    And set android version to 9
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3cb5b31a534ab0c987667f37c976a5cd589d42a5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jul 15 11:40:24 2015 -0700
+
+    doc: Update for 1.6.1
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7338719414ec2c34235c368a55934be0765661c1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 22:33:07 2015 -0700
+
+    Bump version to 1.6.0.4 (android version 8)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 52dc7dc5a791f3e7e307ae11f5c6a20b5bf71ba6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 22:31:47 2015 -0700
+
+    altosdroid: Cleanup Makefile.am
+    
+    Avoid re-creating library symlinks.
+    Make builds depend on resource files too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c4af5cb233013b35d6763f5adf8d11b47f847111
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 22:17:16 2015 -0700
+
+    altosdroid: Clean up tab layout
+    
+    Fuss with weights and gravitys, then add some wrapping layouts to get
+    things looking reasonable on small and larger screens.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e41786fb384892961a6547e17812a24314ce9623
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Jul 11 22:59:34 2015 -0600
+
+    add debian branch spec to vcs-git: line in control so Debian tools work right
+
+commit 251263f72a1c189aac709d3d0410eb916a9f66d6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 20:37:16 2015 -0700
+
+    altosdroid: Add 'Auto' to map tracker list
+    
+    Add an 'auto' menu entry when selecting trackers from the map.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2997c9720f58b2955925e4e99c11a6ec302114a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 20:36:18 2015 -0700
+
+    altosdroid: Note time at startup to avoid flipping trackers
+    
+    Need to set the initial 'switch' time in onStart to avoid flipping
+    between trackers before we've done any other 'switching' action.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2a85f273e33a316bd044c4c8dce17c19633cffe6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 20:21:34 2015 -0700
+
+    Generate Android version info from configure.ac
+    
+    This avoids having version data in two places.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ed682ca39496849b6c0d6bdf81bee6263864895f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:55:43 2015 -0700
+
+    altosdroid: Add other igniter status, various other layout changes
+    
+    Show the first four igniters (A-D) in the pad tab. Make pad and flight
+    layouts look a bit better
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 84021b8e0ab9262262345ce47671c3c0c6c43566
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:54:36 2015 -0700
+
+    altosdroid: Don't disconnect from bluetooth onStartCommand
+    
+    If we've already got a bluetooth connection running, don't slam it
+    shut when the UI starts up again.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bdc953e26ac2dd67021f905807324c6a02e49690
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:54:07 2015 -0700
+
+    altosdroid: Remove a debug line in AltosVoice
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 51bf46248ca7ee3c817e62274b7366258c9f87cf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:53:06 2015 -0700
+
+    altosdroid: Pop up menu of nearby trackers on map click
+    
+    This lets the user select one of potentially many overlapping
+    trackers, and also makes it clear when the current tracker is being
+    changed.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b64ab2a8a25b0c22443bc77829c7f35b4f1c2455
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:17:40 2015 -0700
+
+    altosdroid: Keep speaking even when screen is off
+    
+    Move the voice and telemetry disabling calls from onStop to onDestroy
+    so that a stopped application only leaves off updating the screen, and
+    not the voice bits as well.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d015cfc1499a263549f52d46e9e5b934fcb94f53
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:15:08 2015 -0700
+
+    altoslib: Preload maps based on distance rather than number of tiles
+    
+    This lets you get the specific area requested at all zoom levels,
+    rather than having further detail only at lower resolution zooms.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit afa37e4667ace42c1f43b01b613e639772cfeb75
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:13:37 2015 -0700
+
+    altoslib: Convert longitude from distance in AltosMapTransform
+    
+    This computes the longitude cooresponding to a specific distance at a
+    specific latitude.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1b6f3de0a547fa452d5c40775bcf59c49b229e5e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:11:48 2015 -0700
+
+    altoslib: Limit simultanous map tile downloads to 128
+    
+    Before this change, every tile requested would get downloaded at the
+    same time. With moving to distance-based offline map loading radius
+    values, the number of tiles at closer zooms was in the thousands,
+    overwhelming the network.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b313a5a3d5aba89330c0e20eeac00cc571828953
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:10:44 2015 -0700
+
+    altoslib: Make earth size constants public
+    
+    This lets other code share the values.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a9bc364ecc69d9085146a39198f0671de164eb2e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jul 11 19:09:28 2015 -0700
+
+    altoslib: Make AltosMap floor/ceil static. Check transform in paint
+    
+    This avoids crashing in paint when no transform has been set yet.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 002c523fae9369f0261c28f33152289d965d406b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 25 18:40:21 2015 -0700
+
+    telegps: Add receiver battery to Status display
+    
+    Makes it a bit cramped, but it's useful to have if you're using TeleBT.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3d508b66c2a15286bb9af88e4d92209463e0725d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 25 18:35:07 2015 -0700
+
+    altosui: Correctly show/hide receiver battery value
+    
+    Override the hide() test function which has the listener_state
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f76d5e4fc2ed1e0d79c096cc89793d671ecb78c3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 25 18:33:46 2015 -0700
+
+    altosuilib: Receiver battery voltage lives in listener_state
+    
+    The code to detect whether to show or hide this entry was using
+    functions that weren't given the listener_state and hence returned
+    MISSING all of the time.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7c0f66bf64f410415afaff1b5c8e1443512b7a57
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 25 18:31:29 2015 -0700
+
+    altoslib: Support TeleBT v3.0 battery voltage conversion
+    
+    TeleBT v3.0 uses an STM32 instead of a cc1111, so it needs a different
+    voltage computation from the raw ADC value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ee1b0bd05bedb8a5a631cc79c77fde8fd920ac38
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 25 18:42:29 2015 -0700
+
+    altos/telebt-v3.0: Report battery voltage correctly
+    
+    There was an extra %d, and an extra ':' in the output, making the UI
+    not parse the voltage correctly
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6ecd75a7abb5fcee440f7672082013088634680b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 25 12:23:04 2015 -0700
+
+    altoslib: Don't crash if dragging a map view without any tiles
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 06908e377b7b932bfe3f6dfc840a0a13340f32ce
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 25 12:22:04 2015 -0700
+
+    altosdroid: Class of offline map view widget changed
+    
+    Switch around AltosViewPager to match.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 71d924288d45b09ae655d06df9780ba286e3f3be
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 24 12:02:22 2015 -0700
+
+    altosdroid: Display direction in map view
+    
+    Use direction in map view when available, otherwise use bearing
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c7067f14359d25a8275f2b09e7b30c06c0424dbb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 22:40:11 2015 -0700
+
+    altoslib: Fix replay to run in realtime again
+    
+    At some point, this got sped up to 10x normal speedx
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3e7588e382c70e467b1f328fcfb6bc38a6b79ac7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 22:22:06 2015 -0700
+
+    Bump version to 1.6.0.3
+    
+    Mark the release of 1.6.0.3 altosdroid
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3ce5e24fefaddaa74eadba4722e904354c871387
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 22:21:45 2015 -0700
+
+    altosdroid: Update version numbers in manifest
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d9f96c45d0a3099e9e5fd3c75cc27f9415fcaf55
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 22:21:30 2015 -0700
+
+    altosdroid: Mark tab-dependent voice output as done
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit aed8d3ee2561bbec59b9684fb2042186191302ca
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 22:04:47 2015 -0700
+
+    altosdroid: Make sure flight voice output always starts with 'speed'
+    
+    This resets the flight-mode voice output state back to start again
+    each time the flight tab is selected
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b1b41e0823a60769e7d2d806f4d97ae043d7dae3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 22:00:33 2015 -0700
+
+    altosdroid: Make sure whole flight state is spoken even when no-one is moving
+    
+    This eliminates the case where much of the flight state wasn't
+    reported if the tracker or receiver weren't moving.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f275e73f42e0aaf1760da99fb93c394320aafb84
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 21:47:06 2015 -0700
+
+    altoslib: Typo slipped into AltosState.java
+    
+    Oops.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bfa6cd8934b993bd4a67cfc7a4eeecf9b11915ef
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 21:39:09 2015 -0700
+
+    altosdroid: Change voice output around
+    
+    This makes the voice output depend on the current displayed tab; where
+    the 'recovery' and 'map' tabs get the same value.
+    
+    Pad
+    
+       Reports igniter and GPS status changes
+    
+    Flight
+    
+       Report flight state changes and max height after apogee
+       Report current speed, height and bearing/elevation/range once
+       every 10 seconds while the rocket is in motion.
+    
+    Recovery
+    
+       Report distance and bearing when the location of either the
+       tracker or the receiver changes by more than 10m, but not more
+       than once every 10 seconds.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a761b34ed8fc64435f5a49623f4a5c55e2dda33a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 21:37:25 2015 -0700
+
+    altosdroid: Define strings for the tab names
+    
+    Use these everywhere instead of replicating the same name; might
+    reduce errors.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 56146cd379e1319b7adcf8e22cdda55f771e11be
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 21:35:43 2015 -0700
+
+    altosdroid: Show direction to target in recover tab
+    
+    This takes the bearing to target and current direction of motion (from
+    the Android API) and computes a turn amount and displays that so you
+    don't have to know which way is north when walking towards the rocket.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 60b8bea12edb954e6140a92c8412364c9581e3c2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 23 21:38:37 2015 -0700
+
+    altoslib: Use a longer filter for descent values
+    
+    This makes descent speeds almost useful, a huge improvement
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1513693602c2a4cab0783833d1844c066edabb71
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 22 23:21:05 2015 -0700
+
+    altosdroid: Fix line drawing to old tracker location
+    
+    Selecting an old tracker would often fail to switch the bearing line
+    as it was using the map data instead of just using the local data for
+    the relavant tracker.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 87d2ab135b493486162d33ff172eba1f44dc0ce5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 22 21:04:01 2015 -0700
+
+    altosdroid: Mark four-tab change done
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fb0bd0ec18088ef01549cdb96243d591f618e32b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 22 20:59:17 2015 -0700
+
+    altosdroid: Allow tracker selection from online map widget
+    
+    Need separate tracker selection code for online maps
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2b6768ed32d7be444c49caa40d30b520177bb22a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 22 20:43:23 2015 -0700
+
+    altosdroid: Switch to four tabs (pad/flight/recover/map)
+    
+    Ascent and descent were almost the same; no reason to have both.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0f56903774d9e8bb033dfc0af6945e8ddc1d3065
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 22 20:08:05 2015 -0700
+
+    altosdroid: Select tracker by clicking on map
+    
+    This lets you pick a  tracker from the map, rather than having to use
+    the menu.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a959c1926048d1b96a06aa291131afd7c8e771c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 22 18:41:01 2015 -0700
+
+    altosdroid: Get rid of on-line only maps tab
+    
+    The offline tab did both, so delete the online one and replace it with
+    the offline one.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 18fe64cf2648568dd0bde5acd7b627f1ddb6917e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Jun 22 18:26:34 2015 -0700
+
+    altosdroid: Display online/offline maps in same tab
+    
+    Make the map portion switchable between online and offline maps,
+    leaving the rest of the tab alone.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ee656c9d41238ab2c56859a03fe6b8ce8ff2df4e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 21 10:34:00 2015 -0700
+
+    altosdroid: Add map source preference
+    
+    Not hooked up yet.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9af72a2e629779833ff1787bbfc2ddc8b9d88bba
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 21 09:37:05 2015 -0700
+
+    altosdroid: Show receiver battery voltage in the 'pad' view
+    
+    Helpful to determine when the receiver battery is getting low
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4fbe9d5a1f56178a737ede6b31e8d01a02a7543f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 21 09:36:20 2015 -0700
+
+    altosdroid: Use AltosMap set_zoom_centre
+    
+    This keeps the center of the zoom gesture pinned to the screen.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 08e4e291d32bdb3ac3271a85190d277b1874d277
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 21 09:35:28 2015 -0700
+
+    altosuilib: Use AltosMap set_zoom_centre instead of in-line version
+    
+    Shares the same function with altosdroid this way.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e0c318cdd32b3c3fed5099c754aea3ebc6186a0f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Jun 21 09:34:29 2015 -0700
+
+    altoslib: Add set_zoom_centre to AltosMap
+    
+    This zooms around a specific point, keeping it at the same place on
+    the screen.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b8bdb432aacc1a273ee484a29a24b3768c274db6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 11:58:08 2015 -0700
+
+    altosdroid: Multiple tracker support for google maps
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c72e18fa1713b6e1aa7906210e79dd6354d2390f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 11:57:29 2015 -0700
+
+    altosdroid: Stack map markers with newest rocket on top
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0f40284c554aaadc71a598de8f1c5fe64ea387e1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 11:58:45 2015 -0700
+
+    altosdroid: Support for sorting rockets by age
+    
+    Now we can just sort rockets so that the top-most shown is the newest
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit de785b409e404a5296a7ff2037f52f3029536f28
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 11:57:10 2015 -0700
+
+    altosdroid: Remove debug
+    
+    Just noise at this point.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b2ad3b1ef59fe6e51c8c544f215c33f3b48c3aeb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 11:55:44 2015 -0700
+
+    altosdroid: Switch trackers automatically when changing freq or baud
+    
+    This works by switching trackers when we receive telemetry newer than
+    the last time we changed the frequency configuration.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4a33336b8f468c5b0f2e14c0ee0242c9a24a8b90
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 11:54:20 2015 -0700
+
+    altosuilib: Allow for no transform in map mouse wheel function
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3882e358b6f2970cb1afebcf2a71da34a57002df
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 11:53:24 2015 -0700
+
+    altoslib: Clean up map tile removal
+    
+    Remove them while walking the hash table, rather than creating a list
+    to remove.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0cc03210d5d53d12604688f294b6ca39e3a025de
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 20 09:35:26 2015 -0700
+
+    altoslib/altosuilib: Fix equals methods, add hashCode
+    
+    Whenever we use a class as a HashMap key, that class needs to override
+    the equals(Object) and hashCode() methods. Otherwise, the hash table
+    won't work right.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5568c30f0a4fe346b8ed58934c23653064427d65
+Author: Keith Packard <keithp@aimi.keithp.com>
+Date:   Thu Jun 18 17:37:35 2015 -0700
+
+    ao-bringup: Make turnon_easymega run without 'make install'
+    
+    Use relative paths for all altos tools
+    
+    Signed-off-by: Keith Packard <keithp@aimi.keithp.com>
+
+commit 6cf27ddd5e84824610d6a0bcbb81ba4626b71409
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 18 17:12:41 2015 -0700
+
+    ao-bringup: Use local versions of tools instead of /usr/bin for turnon_easymega
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3e5521070564e9a184f3b781dad9d39cdd963510
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Jun 18 16:56:19 2015 -0700
+
+    ao-bringup: Fail turnon_easymega if accel cal fails
+    
+    This prevents a failing board from being reported as good
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6683146168216aacdc0842934cec1fb48ea03518
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed Jun 17 00:12:06 2015 -0700
+
+    telegps: use new AltosUIFrame constraint helper
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c71abc5c29025eb57fc78968a4ccf8c34cb3a6f2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 16 23:49:45 2015 -0700
+
+    micropeak: Update mac/windows FTDI drivers
+    
+    Use latest FTDI drivers.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1098f7502a603a9cf80ad950f53a2c2abdf7ec93
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 16 23:43:53 2015 -0700
+
+    altosuilib: Create grid-bag helper functions in AltosUIFrame
+    
+    This eases the burden of creating suitable GridBagConstraints values
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cdeefaba7d5ef69f28e5dfb152c5f185f8b85f2e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 16 23:42:59 2015 -0700
+
+    altosuilib: Show state.product if state.device_type isn't set
+    
+    MonitorIdle doesn't get the device type, only the product.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 469be0a57dc9932c26f9c38986d22f6e8b2fd6ed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 16 23:42:22 2015 -0700
+
+    altoslib: Remove debug output from AltosMap
+    
+    It's just annoying now.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 76532162d63239b00a51dd0ff6b1356b07b62d2d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 16 23:41:51 2015 -0700
+
+    altoslib: Expose public function to set state.product
+    
+    Just adds a setter function for this value.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 5cacce8099bfc4fa4019538ac88be00bd2023865
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 16 23:40:34 2015 -0700
+
+    altoslib: Let the application disable the link cancel dialog
+    
+    This lets the application control whether to pop up the cancel dialog
+    when the link isn't working.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 75682a5a18f28acf8f4a61a0d45dad461218186e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 16 23:38:07 2015 -0700
+
+    altoslib: Set product for idle monitor
+    
+    This lets the UI show the product name
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c46c2c5767c6e909fa58587e6c864a4fbaa9fa20
+Merge: 39f4361 6cb7d76
+Author: Robert Garbee <robert@gag.com>
+Date:   Sat Jun 13 17:40:59 2015 -0600
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 39f4361675aa13899864f427a33d4aa48be56cd2
+Author: Robert Garbee <robert@gag.com>
+Date:   Sat Jun 13 17:40:16 2015 -0600
+
+    add beep when test completes
+
+commit 6cb7d76c3fbe32b442713e358654d07b2506897e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 6 20:37:53 2015 -0700
+
+    altosdroid: Resource changes needed for multi-tracker mode
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 78df1d5213c402780fa2ce7e062c64cf5a01c45f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 6 20:37:27 2015 -0700
+
+    altosdroid: Note recent changes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c813c2c8f71017a686128e06b5178fc99ece251c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 6 20:36:18 2015 -0700
+
+    altosdroid: Add multi-tracker support
+    
+    This lets you view multiple trackers in the offline maps tab (online
+    maps not done yet), saves state of each tracker to preferences.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f79d569dfe333621d63a1d4001c85a88f736ad58
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 6 20:35:32 2015 -0700
+
+    altoslib: Add preferences for saving/restoring multiple flight states
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 64ca3d2e7d2b23aedfdf98ef8ebd760bd3291534
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Jun 6 06:00:43 2015 -0700
+
+    ao-tools: Add missing ao-cal-freq man page
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 55753ac8b4b73ec58cb6ef874acc8d606193c5e7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 5 22:52:51 2015 -0700
+
+    ao-bringup: Do telegps freq cal before testing GPS
+    
+    This lets the GPS get some time to see sats while we're messing with
+    the radio frequency calibration.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 527700623cc369cc58e15c29dc1ee374fa4efeb7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 5 22:52:21 2015 -0700
+
+    ao-bringup: Use new ao-cal-freq program for cal-freq script
+    
+    Remove shell script bits that were unreliable.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b9797aa9b6ca38db79c22e4dcefc6efc8a148599
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Jun 5 22:17:02 2015 -0700
+
+    ao-tools: Create ao-cal-freq
+    
+    Create C-based frequency calibration program to replace shell script
+    which isn't reliable.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7c75ec6e11a9287b2360bb62ef4ddb4f0e2083c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Jun 2 12:48:42 2015 -0700
+
+    altosdroid: Highlight age in red when older than 10 seconds
+    
+    This lets you quickly identify stale data
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a533ea525620f194fd89fedad043659bb433d71b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 31 23:09:18 2015 -0700
+
+    altosdroid: Switch from custom title to standard Holo theme
+    
+    This gives us the menu button, which is awfully useful on devices
+    without a hardware version...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0beb02f1848e34892cca6e34ba83d6ca836d6df2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 29 09:49:30 2015 -0700
+
+    altoslib: Require 'debug' hook in AltosMapInterface
+    
+    This lets the map users redirect debug messages as appropriate
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4f1c4dddbce7b4e8673173f1690f79ba60e72ba2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 29 09:43:58 2015 -0700
+
+    altosdroid: use 'show' to set new tab contents in onResume
+    
+    Hook onResume so that newly created/recreated tabs get current
+    contents. The set_visible hook isn't sufficient for that.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 625c496987c2d320a51f3d27f8f00bde17b46a78
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri May 29 09:42:58 2015 -0700
+
+    altosdroid: Missing call to super.onDetach from TabMapOffline
+    
+    Causes an exception when you shut down the application.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 50e709a4088f3d6846fd66cbe9b8c437b3f9c88b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 22:13:39 2015 -0700
+
+    altosdroid: Split out AltosMapView into separate file
+    
+    This lets us use the regular layout configuration bits in the .xml
+    file instead of needing to patch the map object into the display. That
+    was causing problems when re-entering the map tab as the map view
+    would somehow end up with a zero width.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2e424f8dc2886aa475e6ddb21457eba08f768b16
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 22:12:26 2015 -0700
+
+    altosdroid: Add 'Current Location' as an option when preloading maps
+    
+    This lets you load maps around your current location, in case your
+    favorite launch site isn't in the list.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4315b91d7afc2391e3f7444906ac226500bf1345
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 22:08:51 2015 -0700
+
+    altosdroid: Save selected map type in AltosDroid object
+    
+    The map tabs may not have been created when the map type was selected,
+    so save the desired type in the main application object.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 41028472fd2e7e0209125e76b94e551f9d10f89c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 22:06:55 2015 -0700
+
+    altosdroid: Disable debug output on release builds
+    
+    We generate an awful lot of debug spew to the log; presumably that's
+    not helping performance, so lose that for release builds.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e2d54de24df37baa5ff3837334d97f726934ec25
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 22:05:36 2015 -0700
+
+    altosdroid: Look for zipalign in the new place too
+    
+    This build tool moved for some reason...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 85013045ca505096064aaf45c312b158d0263d2a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 01:05:18 2015 -0700
+
+    altosdroid: Add map types and map preloading UIs
+    
+    This adds an ugly dialog to select which maps to preload, and also
+    adds the ability to display other map types.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ba698c2cc48677735046d0881df9c180674e4082
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 01:01:23 2015 -0700
+
+    altoslib: Pass all map loader params to set_load_params
+    
+    Add zoom and map type to the param list so we don't call set_zoom and
+    set_maptype separately, which only causes lots of extra image loads to
+    get started unnecessarily.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c63617415553d97f9be2f19b94365b53d4480c68
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 01:00:47 2015 -0700
+
+    altosuilib: Eliminate extra MapCache in AltosUIMapPreloadNew
+    
+    Use the cache from the map.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ff01fb7275973cdbfd976d3b4e638c6235108121
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 28 01:00:22 2015 -0700
+
+    altosuilib: Get rid of AltosUIMapNew.set_load_params
+    
+    This isn't needed anywhere.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b49b74847ad55e14d1dbf2872ebbe313147e9fd3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 27 23:14:09 2015 -0700
+
+    altosuilib: Switch to altoslib map loading code
+    
+    Remove the map loading code from the UI bits and use the new altoslib
+    version instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3b4e6da65158a434905dc652e46c69d2c38cea7f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 27 23:12:34 2015 -0700
+
+    altoslib: Add map loading helper class
+    
+    This adds the AltosMapLoader class, which iterates over a sequence of
+    zoom levels and formats to get local copies of a desired launch site.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ccd557c846eed37328d6799f36e61308bcbb678a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 27 23:11:58 2015 -0700
+
+    altoslib: Provide toString method for AltosLatLon
+    
+    Makes printing them easier.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 756f501593365b80cfa6f7ca871da3291bbde67a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 27 22:43:53 2015 -0700
+
+    altoslib: Start with map empty
+    
+    Don't load images from 0,0 for no good reason; wait until someone sets
+    a desired lat/lon
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ccba2bb2f193ffd6c3a3d934a46bc06466b4b258
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 27 15:31:25 2015 -0700
+
+    altosuilib: Use altoslib site list loader
+    
+    Removes the custom version and uses the shared code
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 519d477cb752d9cdba78a4daa32b1f547bf889b4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Wed May 27 15:30:29 2015 -0700
+
+    altoslib: Add AltosLaunchSite bits
+    
+    This asynchronously fetches the list of available launch sites from
+    the standard location and notifies the caller when finished.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 103a50db50be55c2293468d273dd94472dd89d94
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 23:05:49 2015 -0700
+
+    altosdroid: Place icons on screen instead of drawing path
+    
+    This makes drawing a bunch faster, and locating stuff on the screen easier.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2509b664df6a13e6ae9e6753dc9fa0d696a4f6c7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 23:05:11 2015 -0700
+
+    altosdroid: Centralize debug printf code
+    
+    Create AltosDebug to hold the debug code, use it everywhere.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bca342577740a9d04b8419ecadcff582e77f1e61
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 23:02:43 2015 -0700
+
+    altosdroid: Move pause before reopening bluetooth into connec thread
+    
+    This avoids stalling the UI while waiting for TBT to boot.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7975d088a4ac44c0943134fa41d0e3b88f50b98f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 19:47:04 2015 -0700
+
+    altosdroid: Add offline map tab
+    
+    It's not very fancy yet, but it does zoom and pan, and show the path
+    of the rocket with a line.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f822b84d8c25159ff113fef6a419b6e18e87a87a
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 01:04:00 2015 -0700
+
+    altosuilib: Get rid of AltosUIVersion.java
+    
+    It's been moved to altoslib/AltosVersion.java
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4895f443e4a748de2677e51869f20c05d265c944
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 00:56:17 2015 -0700
+
+    altosuilib: Remove old map bits
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f41fe2291891b28327c332098bdc601bc75fc4c0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 00:46:21 2015 -0700
+
+    altosuilib: Use new map code for map preload UI
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cd0f4de98ea709e5f070d5f1337658590d2004a1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 00:33:02 2015 -0700
+
+    altosuilib: Add AltosUIImage.java
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 501fa41111b93cc213a1114a33612858e1e93ab5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 26 00:29:53 2015 -0700
+
+    altoslib/altosuilib: Get new Map display code running in altosui and telegps
+    
+    Looks like the display is all hooked up. Still need to replace the
+    preload UIs.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cb23b992be8ba40c97d8988c134a814a13ccd58c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 20:34:05 2015 -0700
+
+    altoslib/altosuilib: Update library version to 7
+    
+    So many ABI/API changes
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 68effc6e39f731a2d7bbe2963999c1e785118897
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 20:33:28 2015 -0700
+
+    altoslib: More frobbing with new map bits
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 00aca369c4070901e0400f291d5f269b5fb8015c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 20:10:37 2015 -0700
+
+    altoslib: Get new abstract mapping code compiling
+    
+    Not useful yet, but at least it compiles now?
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 6ca2c42061b3c0160bf0137c9cd65989c522b826
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 16:35:23 2015 -0700
+
+    altoslib: Build AltosVersion.java in configure.ac
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dcbd87bc685924a6587a5f4dae47d34f417601b0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 16:26:01 2015 -0700
+
+    altos/telelco: Show box voltage with pad knob instead of firing button
+    
+    Turn left past '1' and see the firing box battery voltage instead of
+    pushing the firing button with the box disarmed. This seems like a
+    safer UI.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 91f617d450c187500593d1ae785958187f68ca14
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 15:49:21 2015 -0700
+
+    altos/telelco: Display telefire battery voltage
+    
+    When the firing button is pressed while unarmed, show the telefire
+    battery voltage in the display.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a54aac3361b7bd18f111e5ba06fb89015504b8a4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 15:47:46 2015 -0700
+
+    altos: Add telelco v0.3 (v0.2 with cc1200 instead of cc1120)
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9b268351aee44de959dcc4c792189c10a00428fe
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 25 13:23:54 2015 -0700
+
+    telefire: Report telefire battery voltage over telemetry link
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0e76cb2a7d5db24b6cecdccb6fb8d5bf5527fadf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 24 17:28:07 2015 -0700
+
+    altos: Only set CMAC RSSI value on valid packet received
+    
+    This ignores spurious packets for the purpose of showing the RSSI
+    value in telelco/telefire, avoiding warning about 'low RSSI' when the
+    radio receives noise.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e7c25e3ba04b1e9f8e6fa31e2d464fe96a074dad
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun May 24 17:25:25 2015 -0700
+
+    telefire: Make 'good' RSSI value configurable
+    
+    Different radios will have different 'good' RSSI values, so let each
+    product configure it, leaving the default set to -90dBm, which is
+    a solid signal value for the CC1111.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 29edc37a8de56cb6eb028e3bf3f56aa70f109eba
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu May 21 13:49:28 2015 -0700
+
+    altoslib: Create display-independent map support code
+    
+    This takes the swing-specific map code and creates a generic version.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 59a28811cb19d315b483df296145a2769c445f80
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:25:05 2015 -0700
+
+    Flip version to 1.6.0.2
+    
+    Tag a version for development builds
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3ddaae82215e365726f2a62a3dc46bfb29eb1b5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:23:09 2015 -0700
+
+    micropeak: Use fast load mode by default in -load script
+    
+    Speed up flashing in default config
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit eac71f2b871357ff69581c713059a3741a82a932
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:22:10 2015 -0700
+
+    microsplash: Add 'publish' target to Makefile
+    
+    This dumps the resulting binary and -load script into the Binaries
+    directory of the altusmetrumllc repo
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dba00b3d9102db99592f5822a703e64d98ace8bb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:17:01 2015 -0700
+
+    altos: Support 32MHz xtal on cc1200
+    
+    I ended up building some cc1200-based boards with 32MHz xtals, so just
+    make this an option when building the driver.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fcb523cd083503591fa1277648e5deb258bbbaf4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:16:10 2015 -0700
+
+    doc: Clarify what 'after motor' means
+    
+    Note that this means after motor burn-out, not after motor start.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b67e6ae8ce34ef119da96b442776bb3d78b4f874
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:14:43 2015 -0700
+
+    ao-dump-up: Add --wait option to make testing µPusb easier
+    
+    The --wait option hangs around until a suitable device appears, so
+    that you can test a pile of µPusb devices without needing to
+    constantly interact with the command line.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2b57158737f85c7009658b3e923c66794f01bbdf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:12:52 2015 -0700
+
+    altosui: Remove un-implemented --fetchmaps argument
+    
+    This has been stubbed out for a while, so just remove it.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3e5e9333420ede74d998556c1bbd5888e8ff75ae
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue May 19 10:09:22 2015 -0700
+
+    altoslib: Expose locale and non-locale floating point parsing functions
+    
+    UI bits use locale-specific floating point formats, so parsing those
+    needs to use the locale. Network-based data, like .kml bits need to
+    use non-locale-specific parsing code, so now we've got both APIs
+    available, and each used as appropriate.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3fbf0a29a1b8a67b90ef965ee3e2e972c0ec33a1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon May 18 10:52:24 2015 -0700
+
+    altoslib: Use Locale.ROOT for KML export
+    
+    This avoids locale-specific number formatting, which breaks
+    googleearth when importing the resulting file.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1cc1900e13d79e0451587439c23bbb062d86cee3
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 27 22:29:45 2015 -0700
+
+    altosdroid: Fix tab label height problems
+    
+    With a newer android API versions, we can set the indicator to a View
+    instead of just a string. This lets us wrap the desired string in a
+    TextView and show just that for the indicator, making it exactly the
+    right size.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 7bfa8841b65707d629b425b306ec4cc3acfc156c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Apr 27 21:20:22 2015 -0700
+
+    altosdroid: Add USB support for TeleDongle/TeleBT
+    
+    This lets AltosDroid use a USB-connected receiver as well as Bluetooth devices.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 356617a3476e237311b8bbcefd6beda8271b120d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 29 12:10:47 2015 -0700
+
+    windows: Use new windows stub to launch applications
+    
+    This avoids needing to locate javaw on the system while also making
+    the registry entries less fragile.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fe76229618643f0af7eae965e7a8fc6c70410d27
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Mar 29 12:08:42 2015 -0700
+
+    icon: Convert windows stub into launcher program
+    
+    Instead of an empty windows stub that exists only to hold icons, add
+    useful code that allows it to find and run the related java
+    application. This also adds more resources to that application to
+    provide more information to Windows too.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b1b69c8b73cbffb56c688f6a968d144b642cdff2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Mar 20 15:09:20 2015 -0700
+
+    altos/stmf0: Have fast ADC ring buffer code use wrap-around
+    
+    Instead of requiring that the whole set of returned values fit
+    precisely in the ring, allow for wrap-around so that we can fetch an
+    odd number of ADC values. The previous version required that the fetch
+    amount always be a factor of the ADC buffer size.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 43b4044dc71d44cb25be6397b4d66fd792580eed
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 19 01:12:24 2015 -0700
+
+    altos/chaoskey: Set USB VID/PID to 0x1d50/0x60c6
+    
+    These are allocated from the OpenMoko USB vendor ID page and offer a
+    more 'official' number than from using the 0xfffe space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c3321bd9f73c89686fe983a8d99f4e54fa91550e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Thu Mar 19 01:11:33 2015 -0700
+
+    altos: Add the ability to configure a different USB vendor ID
+    
+    ChaosKey will use an OpenMoko vid/pid, so we need the ability to
+    configure a different USB vendor ID for each product.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fce4e6926de7cb5ef6ea64a8db134c442b86153b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 10 09:35:02 2015 -0600
+
+    ao-tools/ao-list: Show devices that have no TTY
+    
+    chaoskey doesn't advertise itself as a modem, so the kernel doesn't
+    allocate a tty device.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ce99807ef942de54a3f934d321baf3c3d26442bb
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 10 09:34:01 2015 -0600
+
+    altos/test: Support old telemega eeprom file formats in ao_flight_test
+    
+    The old eeprom format used different stoarge for the accel calibration
+    data, which doesn't matter to this code, but the change in the format
+    value does.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f92be7e22150b2de4c899e687d3bbfc1eb842f9e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 10 09:32:20 2015 -0600
+
+    altos/test: Make aprs test code compile again
+    
+    Adding the compressed/uncompressed config option broke the APRS test harness.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 46f2a759dc21ebf3a7bf7e0566903fc1e7364719
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 10 09:30:53 2015 -0600
+
+    ao-tools/ao-mega: Clean up formatting of pyro status messages
+    
+    There was an extra newline and missing space.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2c4ebe9b4b392531cd1a5bbafc4ddc38a9391af5
+Author: Keith Packard <keithp@keithp.com>
+Date:   Tue Mar 10 09:29:52 2015 -0600
+
+    altosui: Add map to MonitorIdle display
+    
+    Nice to be able to verify that maps are working by using this mode,
+    instead of needing to use flight monitoring.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit dec8de9c642fea1df924a667a4779a6c6c8c3453
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 7 16:53:58 2015 -0800
+
+    altos/stmf0: Need ao_exti.h for pin configuration
+    
+    Flash loader uses pin configuration to set up GPIOs for boot selection
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4e53a5da5a8921829a3bb290e7c051950a66ab75
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 7 16:40:30 2015 -0800
+
+    altos: Add makefile for chaoskey flash loader
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 601f33f5e2f833fed9ab10b24a9df91905d7f766
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 7 12:38:08 2015 -0800
+
+    altos: Build chaoskey by default
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f3406f38d71d0c9b55c9a3ae2356a778328509a9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 7 12:36:57 2015 -0800
+
+    altos: Add .gitignore for chaoskey
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4862bec43b29264c361950700f935604f41b840b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 7 12:33:36 2015 -0800
+
+    doc: Update telemetry docs to include new packet formats
+    
+    Add TeleMega TeleMetrum v2 and companion data packet formats.
+    
+    List which radio parts each product uses.
+    
+    Document modulation parameters for new data rates.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cdd7ad469728fde178c69b9c99d70d6e0ab3f12d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Mar 7 10:18:57 2015 -0800
+
+    altosdroid: Deal with bluetooth connection failures better
+    
+    Remember when we've closed the bluetooth connection so that we stop
+    operations, including reporting connection status messages or even
+    starting a connection attempt.
+    
+    Pass the AltosBluetooth object back in connection status messages so
+    that TelemetryService can tell when messages from closed objects get
+    delivered. There's a queue between the two, so the above fix catches
+    most of these instances, but not all of them.
+    
+    Stick a delay during reconnect -- if the TeleBT device is getting
+    power-cycled, it will need a few seconds to reconfigure the device at
+    startup, if AltosDroid manages to connect during that time, the
+    configuration commands will be ignored.
+    
+    Unlock the AltosBluetooth device while we connect so that cancel
+    calls will actually work.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit d446c90dab0aca7e501a0228f24c210758d84a1d
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 2 22:23:22 2015 -0800
+
+    altosui/telegps: Change from variable-units snuck into master
+    
+    show_units_name(double) only exists on the variable-units branch...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bef7c89dac68956a94ae386fa6b87165ab6cb484
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 2 21:16:06 2015 -0800
+
+    altos: Missing pad field in TMv2 data packet
+    
+    The normal ARM padding would have filled this in correctly, but it's
+    best to be explicit about the structure.
+    
+    This also adds a test to make sure the resulting telemetry declaration
+    is exactly 32 bytes,
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 3b133656df4698ceb7af5902711edf9253a29227
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 2 21:11:33 2015 -0800
+
+    altos: Wait for BT disconnect before sending command
+    
+    If AltosDroid manages to connect to the BT module before we've
+    configured it, we won't be able to talk to it as we can't force the
+    module to ignore connection attempts. Wait for AltosDroid to give up
+    and let us configure the device. Eventually, we'll manage, if
+    AltosDroid's delay interval is longer than the time it takes to
+    configure the unit.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 112f528755b6c8a2f6eef3bfec21fac981ffb44f
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 2 21:08:44 2015 -0800
+
+    ao-tools: Add ao-flash-stm32f0x
+    
+    This new script uses openocd to flash stm32f0x parts
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ff3c27e3b842107680dc48084f71eb8c63f1bcab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 2 21:06:51 2015 -0800
+
+    altoslib: Round frequency when configuring radio
+    
+    This makes sure we set the right frequency, instead of being off by
+    1kHz on a regular basis...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e637367e8b940e1642a07b3b7c99147561de9cf1
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 2 21:06:02 2015 -0800
+
+    altosui/telegps: Add config option for APRS format
+    
+    Allow configuration of APRS compressed/uncompressed
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2614d20b324ab215ef22f178e3635d48e757fa9b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Mar 2 21:02:31 2015 -0800
+
+    altos: Make APRS format (compressed/uncompressed) configurable
+    
+    This provides a choice of compressed vs uncompressed when sending APRS
+    packets to deal with receivers that still do not have support for the
+    more useful compressed format.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0724cc334a3bd8d81bbd4641d90a7e4040330efe
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 28 16:06:23 2015 -0800
+
+    altos/usbtrng: Split out random number generating code to separate driver
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit bd18bc5a42fcecfb710477371b9f62610a1ea640
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 28 16:04:00 2015 -0800
+
+    altos: Add chaoskey flash loader
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit a4c436a1c39da971b72d4302623f27af9d56cc38
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 28 16:02:17 2015 -0800
+
+    altos: Create chaoskey product
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 476c5b87ea0901f70fe98b581ecca9afaf957607
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 28 16:00:45 2015 -0800
+
+    altos/stmf0: Allocate USB buffers at startup
+    
+    This lets the extra allocations used from ao_usb_alloc_buffers be
+    allocated before the first USB connection happens.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e4b415cc8f839ceae48916b5e9d78f78589186cf
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 28 16:00:17 2015 -0800
+
+    altos/stmf0: Typo in ao_crc_stm.c
+    
+    Extra semicolon
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 27aef593fb4c037fdb65c9fb397829b42d72d0f2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 28 15:59:30 2015 -0800
+
+    altos/stmf0: Fix fast ADC interface
+    
+    This was configuring the hardware wrong, and wasn't keeping the output
+    ring full.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit ec2d758844202108b446e6b12ec1da8812ceb265
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 28 15:07:16 2015 -0800
+
+    altos: Allow software to offer other USB interface classes than CDC
+    
+    This lets some boards offer non-CDC class USB interfaces so that the
+    modem driver doesn't pick them up.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 4af4e36cda96d053458eeb040e35886890917385
+Merge: 91b1a80 106b16b
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Feb 22 14:55:40 2015 -0700
+
+    Merge branch 'master' of ssh://git.gag.com/scm/git/fw/altos
+
+commit 91b1a80650a7dcd7c5bf819618a8cea0fceb37d9
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sun Feb 8 09:29:01 2015 -0700
+
+    swap names so v3.0 is the default TeleDongle version to turn on
+
+commit 106b16b4d5d024543d7ad8c4b4762151e253f3c4
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 16 22:22:37 2015 -0800
+
+    altosdroid: Ignore automatic tab changing while activity is saved
+    
+    When the activity state is saved (after onSaveInstanceState()), we
+    can't update the UI until the activity is restarted or restored; that
+    means any UI changes we make, like switching tabs, must deal with this
+    by allowing those changes to be ignored, using commitAllowingStateLoss
+    instead of commit.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit e6630ac41ca0d8563cf9a0df5d4acba8192e9624
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 16 21:35:34 2015 -0800
+
+    altosdroid: Missing file: DeviceAddress.java
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 8f2d82461f3cf5da157b23ea45a2fa60d56b196b
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 16 21:32:54 2015 -0800
+
+    altosdroid: Only speak when GUI is running
+    
+    Create voice in onStart, stop it in onStop. This way, if some other
+    application is in use, the voice won't be annoying you.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 877609a60a9f2c61c1efad8285b2a3c22f59be28
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 16 21:19:09 2015 -0800
+
+    altosdroid: Explicitly disconnect BT on termination or 'disconnect'
+    
+    This adds an explicit message to the telemetry service telling it when
+    to stop trying to talk to the bluetooth device. Until this message is
+    received, the service will reconnect to the specified BT device.
+    
+    That message is sent when you 'quit' the application, or when you 'disconnect'.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c51d39c7ea1153cd2d0dc02c47824a9f35b22fb9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 16 20:57:11 2015 -0800
+
+    altosdroid: Lots of bluetooth connection changes
+    
+    Appears to more reliably abort in-progress connection attempts so you
+    can switch TBT devices without having the previous device in
+    operation.
+    
+    Shows which device the connection is being attempted for.
+    
+    Eliminate the 10-second timer and just disable the service when the GUI
+    shuts down while no BT connection is running.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit b13a78e4f457f67605fc6dafc7f9733746a4f70c
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 16 20:49:39 2015 -0800
+
+    ao-bringup: Changes for telebt for keith's hacking
+    
+    Make the programming device auto-detect by username.
+    Load binary from ~/altusmetrumllc
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 2ebb4dff758058ae9512cf36518416eb69b928f0
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sun Feb 15 08:57:55 2015 -0800
+
+    altos: Remove some accidental debug printfs from ao_packet.c
+    
+    While fixing the cc1200 configuration, I added some debug printfs to
+    this code. They were accidentally committed with the fix...
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 135abf0e7c5ceb5738a0b5f68fe2be4b7abdae5e
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 14 23:18:38 2015 -0800
+
+    altos/cc1200: Adjust bit-sync configuration
+    
+    The default bit timing adjustment mechanism allows for only a 0.2%
+    deviation from the programmed bit timing. I found one TeleMini device
+    which is beyond that tolerance as it was built with an older crystal
+    with more error.
+    
+    Switch to the more expensive synchronization mechanism which allows up
+    to 2% timing error, but requires a multi-byte preamble (which we
+    have). This fixes packet mode nicely.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 9c75faf1ec51eb2f9a8dc9402653490143a784d9
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 14 08:35:47 2015 -0800
+
+    altos: embed ao_alarm and ao_clear_alarm in ao_sleep_for
+    
+    sdcc won't embed these itself, and thus consumes too much flash for
+    telemetrum-v1.0
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit cc64e0e9d35e01b349680159a5bbd68d059134cd
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 14 01:16:42 2015 -0800
+
+    ao-bringup/turnon_telemini: Detect which programmer to use by $USER
+    
+    Bdale uses TD 100, keithp uses TD 186.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit c4f9d96bdea192486f0e3f2d80b846c39a05c0ab
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 14 01:13:21 2015 -0800
+
+    altosuilib: Detect pair programming by product name, not USB id
+    
+    With TeleDongle, TeleBT and TeleMetrum coming in both pair- and self-
+    programmable versions, we can't use the USB id to tell them
+    apart. Instead, fetch the device name and use that instead.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 0e929ee2d0a3d1b1bacd36c2c3723ab860eb40b6
+Author: Keith Packard <keithp@keithp.com>
+Date:   Sat Feb 14 01:11:30 2015 -0800
+
+    altosui: Run all igniter status requests from non-GUI thread
+    
+    Anything run from the UI thread blocks the UI entirely; the Fire
+    Igniters startup code to collect the number of pyro channels when
+    building the UI was doing that from the UI thread. Switch that around
+    so that the UI doesn't get built until that reply comes back, allowing
+    the user to see the 'connecting' dialog, and cancel it if required.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit f4c812bef76a2cd95f675cb27ea89059561ceec7
+Author: Keith Packard <keithp@keithp.com>
+Date:   Fri Feb 13 23:51:10 2015 -0800
+
+    altos: Replace ao_alarm/ao_clear_alarm with ao_sleep_for
+    
+    Having arbitrary alarms firing in the middle of complicated device
+    logic makes no sense at all. Therefore only correct use of ao_alarm
+    and ao_clear_alarm was around a specific ao_sleep call, with correct
+    recovery in case the alarm fires.
+    
+    This patch replaces all uses of ao_alarm/ao_sleep/ao_clear_alarm with
+    ao_sleep_for, a new function which takes the alarm timeout directly.
+    
+    A few cases which weren't simply calling ao_sleep have been reworked
+    to pass the timeout value down to the place where sleep *is* being
+    called, and having that code deal with the return correctly.
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit 1445725b983134d5a967dee88ef997bf15d4a422
+Author: Tom Marble <tmarble@info9.net>
+Date:   Wed Feb 11 08:21:27 2015 -0600
+
+    Added continuous output option to ao-usbtrng
+
+commit 65837616a6d073da8e3e2bf9da524a48cffb77c2
+Author: Keith Packard <keithp@keithp.com>
+Date:   Mon Feb 9 07:28:18 2015 -0800
+
+    altos/stmf0: Add ao_crc_stm.c
+    
+    Tom discovered that this was missing
+    
+    Signed-off-by: Keith Packard <keithp@keithp.com>
+
+commit fa813bcb6afc851cf4029b56c19ba46a3ae578f5
+Author: Tom Marble <tmarble@info9.net>
+Date:   Mon Feb 9 08:35:24 2015 -0600
+
+    Minor typo in man page
+
+commit e2cefd8593d269ce603aaf33f4a53a5c2dcb3350
+Author: Bdale Garbee <bdale@gag.com>
+Date:   Sat Feb 7 22:36:22 2015 -0700
+
+    update ChangeLog for release
+
 commit 26f61380ce6b4df80fa0b5a8a242cef79d5ae339
 Author: Bdale Garbee <bdale@gag.com>
 Date:   Sat Feb 7 22:23:38 2015 -0700
diff --git a/altosdroid/AndroidManifest.xml b/altosdroid/AndroidManifest.xml
deleted file mode 100644 (file)
index 19e5a6d..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-<?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="6"
-          android:versionName="1.5">
-    <uses-sdk android:targetSdkVersion="10" android:minSdkVersion="10"/>
-    <!-- Google Maps -->
-    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
-
-    <!-- Permissions needed to access bluetooth -->
-    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
-    <uses-permission android:name="android.permission.BLUETOOTH" />
-    <!-- Permissions needed to save Telemetry logs to SD card -->
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-    <!-- Permissions needed for GoogleMaps -->
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
-    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
-    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
-
-    <permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
-                android:protectionLevel="signature"/>
-    <uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
-
-
-    <application android:label="@string/app_name"
-                 android:icon="@drawable/app_icon"
-                 android:allowBackup="true" >
-        <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
-                  android:label="@string/app_name"
-                  android:configChanges="orientation|keyboardHidden" >
-            <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" />
-
-        <meta-data android:name="com.google.android.maps.v2.API_KEY"
-                   android:value="AIzaSyDSr6u4i9TJmVGhgGk4g0wUUhTy9FGyn0s"/>
-        <meta-data android:name="com.google.android.gms.version"
-                   android:value="@integer/google_play_services_version" />
-    </application>
-</manifest>
diff --git a/altosdroid/AndroidManifest.xml.in b/altosdroid/AndroidManifest.xml.in
new file mode 100644 (file)
index 0000000..2403579
--- /dev/null
@@ -0,0 +1,92 @@
+<?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="@ANDROID_VERSION@"
+          android:versionName="@VERSION@">
+    <uses-sdk android:targetSdkVersion="12" android:minSdkVersion="12"/>
+    <!-- Google Maps -->
+    <uses-feature android:glEsVersion="0x00020000" android:required="true"/>
+
+    <!-- Permissions needed to access bluetooth -->
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+    <!-- Permissions needed to save Telemetry logs to SD card -->
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <!-- Permissions needed for GoogleMaps -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+    <permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"
+                android:protectionLevel="signature"/>
+    <uses-permission android:name="org.altusmetrum.AltosDroid.permission.MAPS_RECEIVE"/>
+
+    <!-- Permissions needed to access USB OTG -->
+    <uses-feature android:name="android.hardware.usb.host" />
+
+    <application android:label="@string/app_name"
+                 android:icon="@drawable/app_icon"
+                 android:allowBackup="true"
+                android:theme="@style/CustomTheme">
+        <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
+                  android:label="@string/app_name"
+                  android:configChanges="orientation|keyboardHidden"
+                 android:launchMode="singleTop">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+       <activity android:name="org.altusmetrum.AltosDroid.AltosDroid"
+                  android:configChanges="orientation|keyboardHidden"
+                 android:launchMode="singleTop">
+         <intent-filter>
+           <action
+               android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
+         </intent-filter>
+         <meta-data
+             android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
+             android:resource="@xml/device_filter" />
+       </activity>
+
+        <activity android:name=".DeviceListActivity"
+                  android:label="@string/select_device"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+        <activity android:name=".PreloadMapActivity"
+                  android:label="@string/preload_maps"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+        <activity android:name=".MapTypeActivity"
+                  android:label="@string/map_type"
+                  android:theme="@android:style/Theme.Dialog"
+                  android:configChanges="orientation|keyboardHidden" />
+
+        <service android:name=".TelemetryService" />
+
+        <meta-data android:name="com.google.android.maps.v2.API_KEY"
+                   android:value="AIzaSyDSr6u4i9TJmVGhgGk4g0wUUhTy9FGyn0s"/>
+        <meta-data android:name="com.google.android.gms.version"
+                   android:value="@integer/google_play_services_version" />
+    </application>
+</manifest>
index 361de13cb3b917a44d2ece35ec733fdda91b1c06..26e14ee7edaa5879525d0f6ffec72267243aa300 100644 (file)
@@ -14,11 +14,16 @@ DX=$(SDK)/platform-tools/dx
 ADB=$(SDK)/platform-tools/adb
 AAPT=$(SDK)/platform-tools/aapt
 APKBUILDER=$(SDK)/tools/apkbuilder
-ZIPALIGN=$(SDK)/tools/zipalign
+ZIPALIGN_A=$(SDK)/tools/zipalign
+ZIPALIGN_B=$(SDK)/build-tools/*/zipalign
 
 JAVA_SRC_DIR=src/org/altusmetrum/AltosDroid
 EXT_LIBDIR=libs
 DRAWABLE_DIR=res/drawable
+LAYOUT_DIR=res/layout
+MENU_DIR=res/menu
+VALUES_DIR=res/values
+XML_DIR=res/xml
 ALTOSLIB_SRCDIR=../altoslib
 ALTOSLIB_JAR=altoslib_$(ALTOSLIB_VERSION).jar
 
@@ -39,19 +44,25 @@ DRAWABLES=\
     $(DRAWABLE_DIR)/greenled.png \
     $(DRAWABLE_DIR)/grayled.png
 
-SRC=$(JAVA_SRC) $(DRAWABLES)
+LAYOUTS=$(LAYOUT_DIR)/*.xml
+MENUS=$(MENU_DIR)/*.xml
+VALUES=$(VALUES_DIR)/*.xml
+XMLS=$(XML_DIR)/*.xml
+
+RES=$(LAYOUTS) $(MENUS) $(VALUES) $(XMLS)
+
+SRC=$(JAVA_SRC) $(DRAWABLES) $(RES)
 
 all: $(all_target)
 
 .NOTPARALLEL:
 
-$(EXT_LIBDIR):
+$(ALTOSLIB): $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR)
        mkdir -p $(EXT_LIBDIR)
-
-$(ALTOSLIB): $(EXT_LIBDIR) $(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR)
        cd $(EXT_LIBDIR) && ln -sf $(shell echo $(EXT_LIBDIR) | sed 's|[^/]\+|..|g')/$(ALTOSLIB_SRCDIR)/$(ALTOSLIB_JAR) .
 
-$(SUPPORT_V4): $(EXT_LIBDIR) $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR)
+$(SUPPORT_V4): $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR)
+       mkdir -p $(EXT_LIBDIR)
        cd $(EXT_LIBDIR) && ln -sf $(SUPPORT_V4_SRCDIR)/$(SUPPORT_V4_JAR) .
 
 $(GOOGLE_PLAY_SERVICES_LIB): $(GOOGLE_PLAY_SERVICES_LIB_SRCDIR)/$(GOOGLE_PLAY_SERVICES_LIB)
@@ -83,9 +94,15 @@ bin/AltosDroid-release.apk: bin/AltosDroid-release-unsigned.apk
           -storepass:file ~/altusmetrumllc/google-play-passphrase \
           -signedjar bin/AltosDroid-release-signed.apk \
           bin/AltosDroid-release-unsigned.apk AltosDroid
-       $(ZIPALIGN) -f 4 \
-          bin/AltosDroid-release-signed.apk \
-          bin/AltosDroid-release.apk
+       if [ -f $(ZIPALIGN_A) ]; then \
+               $(ZIPALIGN_A) -f 4 \
+                  bin/AltosDroid-release-signed.apk \
+                  bin/AltosDroid-release.apk; \
+       else \
+               $(ZIPALIGN_B) -f 4 \
+                  bin/AltosDroid-release-signed.apk \
+                  bin/AltosDroid-release.apk; \
+       fi
 
 release: bin/AltosDroid-release.apk
 
index 6a246df7fe3dca22e5cc60632ee0ca8c8ed0697d..73b5ed27278392d61afad82caa1bd18147853035 100644 (file)
@@ -15,17 +15,9 @@ Desired AltosDroid feature list
 
  *) Monitor-idle mode
 
- *) Frequency scanning
+ *) Online maps comes up tracking object at 0,0
 
- *) Select satellite imaging mode
-
- *) TeleBT battery voltage
-
- *) Deal with long bluetooth list. Currently, a list longer than
-    the screen makes it impossible to use entries off the bottom.
-
- *) Pickle/unpickle state instead of reloading entire history from
-    file. Current restart time is lengthy.
+ *) Have names for each serial number, default to callsign
 
 Completed features
 
@@ -47,3 +39,70 @@ Completed features
 
        Done
 
+ *) Select satellite imaging mode
+
+       Done
+
+ *) Deal with long bluetooth list. Currently, a list longer than
+    the screen makes it impossible to use entries off the bottom.
+
+       Done
+
+ *) Pickle/unpickle state instead of reloading entire history from
+    file. Current restart time is lengthy.
+
+       Done
+
+ *) Offline maps
+
+       Done
+
+ *) Multi-tracker management
+
+       Done
+
+ *) Provide units for age field, turn red if old
+
+       Done
+
+ *) TeleBT battery voltage
+
+       Done
+
+ *) Evaluate performance issues
+
+       Done. Offline maps were duplicating tabs at every redisplay.
+
+ *) Merge offline/online maps into single tab with mode
+
+       Done.
+
+ *) Auto select tracker after long delay
+
+       Done.
+
+ *) Select tracker by clicking map
+
+       Done.
+
+ *) Convert to four tab design:
+
+       Done.
+
+ *) Make voice responses depend on selected tab
+
+       Done.
+
+ *) Monitor TeleMega igniters
+
+       Done. Visible only in Pad tab
+
+ *) Make it harder to switch trackers in map view. Too easy to touch
+    the screen and switch on accident.
+
+       Done. A menu pops up with trackers within a small radius of
+       the touch point, letting you cancel if that wasn't your intent.
+
+ *) Make sure it keeps talking with the screen blanked
+
+       Done. Don't shut down voice when stopping UI.
index 66db0d159b1fe234f3fe34fc2a0743ca579d84c9..3ac252342cbe9ef944aee82ab310cf77d8366cdd 100644 (file)
@@ -8,4 +8,4 @@
 # project structure.
 
 # Project target.
-target=android-10
+target=android-12
index 96b9551ce02b63037f437646af32581a9c4e5a5a..d178f98a978e2159ce92c182fe3edf30e9ace47f 100644 (file)
@@ -8,5 +8,5 @@
 # project structure.
 
 # Project target.
-target=android-10
+target=android-12
 android.library.reference.1=google-play-services_lib/
diff --git a/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png
new file mode 100644 (file)
index 0000000..bc9160d
Binary files /dev/null and b/altosdroid/res/drawable-hdpi/ic_maps_indicator_current_position.png differ
diff --git a/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png b/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png
new file mode 100644 (file)
index 0000000..4e427d8
Binary files /dev/null and b/altosdroid/res/drawable-mdpi/ic_maps_indicator_current_position.png differ
index 93d655172d608655323207004068f85d59b49626..bf295e4c5e0fc8eba7a146cefb66c8d1902d78c5 100644 (file)
         android:textColor="#fff"
         android:paddingLeft="5dp"
     />
+    <ListView android:id="@+id/new_devices"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+       android:fadeScrollbars="false"
+       android:scrollbars="vertical"
+    />
     <TextView android:id="@+id/title_paired_devices"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
     <ListView android:id="@+id/paired_devices"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:stackFromBottom="true"
         android:layout_weight="1"
-    />
-    <ListView android:id="@+id/new_devices"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:stackFromBottom="true"
-        android:layout_weight="2"
+       android:fadeScrollbars="false"
+       android:scrollbars="vertical"
     />
 </LinearLayout>
diff --git a/altosdroid/res/layout/map_preload.xml b/altosdroid/res/layout/map_preload.xml
new file mode 100644 (file)
index 0000000..dc613bf
--- /dev/null
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+  <ScrollView android:layout_width="fill_parent"
+             android:layout_height="wrap_content">
+    <LinearLayout android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+                  android:orientation="vertical">
+      <TextView android:id="@+id/preload_site_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_site_label"
+               />
+      <Spinner android:id="@+id/preload_site_list"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_site_label"
+              android:spinnerMode="dropdown"
+              />
+      <TextView android:id="@+id/preload_latitude_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_latitude_label"
+               />
+      <EditText android:id="@+id/preload_latitude"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:hint="@string/preload_latitude_label"
+               android:inputType="number"/>
+      <TextView android:id="@+id/preload_longitude_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_longitude_label"
+               />
+      <EditText android:id="@+id/preload_longitude"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:hint="@string/preload_longitude_label"
+               android:inputType="number"/>
+      <TextView android:id="@+id/preload_types"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_types"
+               />
+      <CheckBox android:id="@+id/preload_hybrid"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_hybrid"
+               />
+      <CheckBox android:id="@+id/preload_satellite"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_satellite"
+               />
+      <CheckBox android:id="@+id/preload_roadmap"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_roadmap"
+               />
+      <CheckBox android:id="@+id/preload_terrain"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_terrain"
+               />
+      <TextView android:id="@+id/preload_min_zoom_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_min_zoom"
+               />
+      <Spinner android:id="@+id/preload_min_zoom"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_min_zoom"
+              android:spinnerMode="dropdown"
+              />
+      <TextView android:id="@+id/preload_max_zoom_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_max_zoom"
+               />
+      <Spinner android:id="@+id/preload_max_zoom"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_max_zoom"
+              android:spinnerMode="dropdown"
+              />
+      <TextView android:id="@+id/preload_radius_label"
+               android:layout_width="fill_parent"
+               android:layout_height="wrap_content"
+               android:text="@string/preload_radius"
+               />
+      <Spinner android:id="@+id/preload_radius"
+              android:layout_width="fill_parent"
+              android:layout_height="wrap_content"
+              android:prompt="@string/preload_radius"
+              android:spinnerMode="dropdown"
+              />
+      <Button android:id="@+id/preload_load"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:text="@string/preload_load"
+             />
+      <ProgressBar android:id="@+id/preload_progress"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  style="@android:style/Widget.ProgressBar.Horizontal"
+                  />
+    </LinearLayout>
+  </ScrollView>
+</LinearLayout>
diff --git a/altosdroid/res/layout/map_type.xml b/altosdroid/res/layout/map_type.xml
new file mode 100644 (file)
index 0000000..610e6bb
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
+
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    >
+  <Button android:id="@+id/map_type_hybrid"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_hybrid"
+          android:onClick="selectType"
+          />
+  <Button android:id="@+id/map_type_satellite"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_satellite"
+          android:onClick="selectType"
+          />
+  <Button android:id="@+id/map_type_roadmap"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_roadmap"
+          android:onClick="selectType"
+          />
+  <Button android:id="@+id/map_type_terrain"
+          android:layout_width="fill_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/preload_terrain"
+          android:onClick="selectType"
+          />
+</LinearLayout>
diff --git a/altosdroid/res/layout/tab_ascent.xml b/altosdroid/res/layout/tab_ascent.xml
deleted file mode 100644 (file)
index b21ec42..0000000
+++ /dev/null
@@ -1,299 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright © 2013 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:orientation="vertical" >
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal"
-               android:paddingTop="5dp" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/max_height_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/max_height_label" />
-
-                       <TextView
-                               android:id="@+id/max_height_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@id/max_height_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal"
-               android:paddingTop="5dp" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/max_speed_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/max_speed_label" />
-
-                       <TextView
-                               android:id="@+id/max_speed_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@id/max_speed_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal"
-               android:paddingTop="5dp" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/max_accel_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/max_accel_label" />
-
-                       <TextView
-                               android:id="@+id/max_accel_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@id/max_accel_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-       </LinearLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/lat_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/latitude_label" />
-
-               <TextView
-                       android:id="@+id/lat_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/lat_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/lon_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/longitude_label" />
-
-               <TextView
-                       android:id="@+id/lon_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/lon_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/apogee_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/apogee_voltage_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/apogee_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/apogee_redled"
-                       android:contentDescription="@string/apogee_voltage_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/apogee_voltage_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/apogee_greenled"
-                       android:text="@string/apogee_voltage_label" />
-
-               <TextView
-                       android:id="@+id/apogee_voltage_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/apogee_voltage_label"
-                       android:layout_toRightOf="@id/apogee_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/main_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/main_voltage_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/main_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/main_redled"
-                       android:contentDescription="@string/main_voltage_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/main_voltage_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/main_greenled"
-                       android:text="@string/main_voltage_label" />
-
-               <TextView
-                       android:id="@+id/main_voltage_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/main_voltage_label"
-                       android:layout_toRightOf="@id/main_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/altosdroid/res/layout/tab_descent.xml b/altosdroid/res/layout/tab_descent.xml
deleted file mode 100644 (file)
index 9e1fc82..0000000
+++ /dev/null
@@ -1,339 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright © 2013 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:orientation="vertical" >
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal"
-               android:paddingTop="5dp" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal"
-               android:paddingTop="5dp" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <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/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/compass_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="" />
-
-                       <TextView
-                               android:id="@+id/compass_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@id/compass_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:layout_weight="0"
-               android:baselineAligned="true"
-               android:orientation="horizontal"
-               android:paddingTop="5dp" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-
-                       <TextView
-                               android:id="@+id/distance_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:text="@string/gnd_distance_label" />
-
-                       <TextView
-                               android:id="@+id/distance_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_alignParentRight="true"
-                               android:layout_below="@id/distance_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <TextView
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1" >
-               </TextView>
-
-       </LinearLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/lat_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/latitude_label" />
-
-               <TextView
-                       android:id="@+id/lat_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/lat_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/lon_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/longitude_label" />
-
-               <TextView
-                       android:id="@+id/lon_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/lon_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/apogee_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/apogee_voltage_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/apogee_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/apogee_redled"
-                       android:contentDescription="@string/apogee_voltage_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/apogee_voltage_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/apogee_greenled"
-                       android:text="@string/apogee_voltage_label" />
-
-               <TextView
-                       android:id="@+id/apogee_voltage_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/apogee_voltage_label"
-                       android:layout_toRightOf="@id/apogee_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/main_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/main_voltage_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/main_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/main_redled"
-                       android:contentDescription="@string/main_voltage_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/main_voltage_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/main_greenled"
-                       android:text="@string/main_voltage_label" />
-
-               <TextView
-                       android:id="@+id/main_voltage_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/main_voltage_label"
-                       android:layout_toRightOf="@id/main_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-</LinearLayout>
diff --git a/altosdroid/res/layout/tab_flight.xml b/altosdroid/res/layout/tab_flight.xml
new file mode 100644 (file)
index 0000000..85c171b
--- /dev/null
@@ -0,0 +1,402 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright © 2013 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+  <LinearLayout
+      android:layout_weight="0"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical" >
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <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/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <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/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/max_speed_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/max_speed_label" />
+
+       <TextView
+           android:id="@+id/max_speed_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/max_speed_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/max_height_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/max_height_label" />
+
+       <TextView
+           android:id="@+id/max_height_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/max_height_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal"
+       android:paddingTop="5dp" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <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/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <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/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal"
+       android:paddingTop="5dp" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <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/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/compass_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="" />
+
+       <TextView
+           android:id="@+id/compass_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/compass_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+    </LinearLayout>
+
+    <LinearLayout
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="0"
+       android:baselineAligned="true"
+       android:orientation="horizontal"
+       android:paddingTop="5dp" >
+
+      <RelativeLayout
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+
+       <TextView
+           android:id="@+id/distance_label"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:text="@string/gnd_distance_label" />
+
+       <TextView
+           android:id="@+id/distance_value"
+           android:layout_width="wrap_content"
+           android:layout_height="wrap_content"
+           android:layout_alignParentRight="true"
+           android:layout_below="@id/distance_label"
+           android:text=""
+           android:textAppearance="?android:attr/textAppearanceSmall" />
+      </RelativeLayout>
+
+      <TextView
+         android:layout_width="0dp"
+         android:layout_height="wrap_content"
+         android:layout_weight="1" >
+      </TextView>
+
+    </LinearLayout>
+
+    <RelativeLayout
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/latitude_label" />
+
+      <TextView
+         android:id="@+id/lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/longitude_label" />
+
+      <TextView
+         android:id="@+id/lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:id="@+id/apogee_view"
+       android:visibility="gone"
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <ImageView
+         android:id="@+id/apogee_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/apogee_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/apogee_redled"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:paddingRight="5dp"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/apogee_greenled"
+         android:text="@string/apogee_voltage_label" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:id="@+id/main_view"
+       android:visibility="gone"
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content"
+       android:paddingTop="5dp" >
+
+      <ImageView
+         android:id="@+id/main_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/main_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/main_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/main_redled"
+         android:contentDescription="@string/main_voltage_label"
+         android:paddingRight="5dp"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/main_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/main_greenled"
+         android:text="@string/main_voltage_label" />
+
+      <TextView
+         android:id="@+id/main_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+</LinearLayout>
diff --git a/altosdroid/res/layout/tab_landed.xml b/altosdroid/res/layout/tab_landed.xml
deleted file mode 100644 (file)
index f27baa9..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright © 2013 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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:orientation="vertical" >
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content" >
-
-               <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/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/distance_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/distance_label" />
-
-               <TextView
-                       android:id="@+id/distance_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@+id/distance_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/target_lat_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/target_latitude_label" />
-
-               <TextView
-                       android:id="@+id/target_lat_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/target_lat_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/target_lon_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/target_longitude_label" />
-
-               <TextView
-                       android:id="@+id/target_lon_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/target_lon_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/receiver_lat_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/receiver_latitude_label" />
-
-               <TextView
-                       android:id="@+id/receiver_lat_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/receiver_lat_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/receiver_lon_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/receiver_longitude_label" />
-
-               <TextView
-                       android:id="@+id/receiver_lon_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/receiver_lon_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/max_height_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/max_height_label" />
-
-               <TextView
-                       android:id="@+id/max_height_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/max_height_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/max_speed_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/max_speed_label" />
-
-               <TextView
-                       android:id="@+id/max_speed_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/max_speed_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <TextView
-                       android:id="@+id/max_accel_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:text="@string/max_accel_label" />
-
-               <TextView
-                       android:id="@+id/max_accel_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_alignParentRight="true"
-                       android:layout_below="@id/max_accel_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
diff --git a/altosdroid/res/layout/tab_layout.xml b/altosdroid/res/layout/tab_layout.xml
new file mode 100644 (file)
index 0000000..2c21c64
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/customTabLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:id="@+id/tabLabel"
+        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:textSize="13dp"
+        android:textColor="#ffffff"
+       android:gravity="center_horizontal"
+       android:background="#808080"
+        android:layout_centerVertical="true"
+        android:layout_centerHorizontal="true" />
+</RelativeLayout>
index f611ae481b423bb821f364d351f6fcec98c994d8..952abd494dd80066fb6c276c6796986491f03299 100644 (file)
        android:layout_height="match_parent"
        android:orientation="vertical" >
 
-       <LinearLayout
-           android:id="@+id/map"
-               android:orientation="horizontal"
-               android:layout_width="fill_parent"
-               android:layout_height="0dp"
-               android:layout_weight="1">
-
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:baselineAligned="true"
-               android:orientation="horizontal" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1"
-                       android:paddingTop="5dp" >
-
-                       <TextView
-                               android:id="@+id/distance_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:paddingRight="4dp"
-                               android:text="@string/distance_label" />
-
-                       <TextView
-                               android:id="@+id/distance_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_toRightOf="@+id/distance_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1"
-                       android:paddingTop="5dp" >
-
-                       <TextView
-                               android:id="@+id/bearing_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:paddingRight="4dp"
-                               android:text="@string/bearing_label" />
-
-                       <TextView
-                               android:id="@+id/bearing_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_toRightOf="@+id/bearing_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:baselineAligned="true"
-               android:orientation="horizontal" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1"
-                       android:paddingTop="5dp" >
-
-                       <TextView
-                               android:id="@+id/target_lat_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:paddingRight="4dp"
-                               android:text="@string/target_latitude_label" />
-
-                       <TextView
-                               android:id="@+id/target_lat_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_toRightOf="@id/target_lat_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1"
-                       android:paddingTop="5dp" >
-
-                       <TextView
-                               android:id="@+id/target_lon_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:paddingRight="4dp"
-                               android:text="@string/target_longitude_label" />
-
-                       <TextView
-                               android:id="@+id/target_lon_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_toRightOf="@id/target_lon_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-       </LinearLayout>
-
-       <LinearLayout
-               xmlns:android="http://schemas.android.com/apk/res/android"
-               android:layout_width="fill_parent"
-               android:layout_height="wrap_content"
-               android:baselineAligned="true"
-               android:orientation="horizontal" >
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1"
-                       android:paddingTop="5dp" >
-
-                       <TextView
-                               android:id="@+id/receiver_lat_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:paddingRight="4dp"
-                               android:text="@string/receiver_latitude_label" />
-
-                       <TextView
-                               android:id="@+id/receiver_lat_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_toRightOf="@id/receiver_lat_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-
-               <RelativeLayout
-                       android:layout_width="0dp"
-                       android:layout_height="wrap_content"
-                       android:layout_weight="1"
-                       android:paddingTop="5dp" >
-
-                       <TextView
-                               android:id="@+id/receiver_lon_label"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:paddingRight="4dp"
-                               android:text="@string/receiver_longitude_label" />
-
-                       <TextView
-                               android:id="@+id/receiver_lon_value"
-                               android:layout_width="wrap_content"
-                               android:layout_height="wrap_content"
-                               android:layout_toRightOf="@id/receiver_lon_label"
-                               android:text=""
-                               android:textAppearance="?android:attr/textAppearanceSmall" />
-               </RelativeLayout>
-       </LinearLayout>
-</LinearLayout>
\ No newline at end of file
+  <FrameLayout
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal"
+      android:layout_weight="1">
+
+    <LinearLayout
+       android:id="@+id/map_online"
+       android:orientation="horizontal"
+       android:layout_width="fill_parent"
+       android:layout_height="fill_parent"
+       android:layout_weight="1">
+    </LinearLayout>
+
+    <org.altusmetrum.AltosDroid.AltosMapOffline
+       android:id="@+id/map_offline"
+       android:layout_width="fill_parent"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"/>
+  </FrameLayout>
+  
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal" >
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/distance_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/distance_label" />
+
+      <TextView
+         android:id="@+id/distance_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/distance_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/bearing_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/bearing_label" />
+
+      <TextView
+         android:id="@+id/bearing_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/bearing_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+  </LinearLayout>
+
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal" >
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/target_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/target_latitude_label" />
+
+      <TextView
+         android:id="@+id/target_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/target_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/target_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/target_longitude_label" />
+
+      <TextView
+         android:id="@+id/target_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/target_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:baselineAligned="true"
+      android:orientation="horizontal" >
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/receiver_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/receiver_latitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/receiver_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_width="0dp"
+       android:layout_height="wrap_content"
+       android:layout_weight="1"
+       android:paddingTop="5dp" >
+
+      <TextView
+         android:id="@+id/receiver_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:paddingRight="4dp"
+         android:text="@string/receiver_longitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_toRightOf="@id/receiver_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+</LinearLayout>
index 38e61f836c2cb097201782f32d1811c4dc083d4a..88648c3bedce81727afbd369880a89428e6e4a16 100644 (file)
      with this program; if not, write to the Free Software Foundation, Inc.,
      59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-       android:layout_width="fill_parent"
-       android:layout_height="wrap_content"
-       android:layout_weight="0"
-       android:orientation="vertical" >
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content" >
-
-               <ImageView
-                       android:id="@+id/battery_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/battery_voltage_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/battery_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/battery_redled"
-                       android:contentDescription="@string/battery_voltage_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/battery_voltage_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/battery_greenled"
-                       android:text="@string/battery_voltage_label" />
-
-               <TextView
-                       android:id="@+id/battery_voltage_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_below="@id/battery_voltage_label"
-                       android:layout_toRightOf="@id/battery_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/apogee_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/apogee_voltage_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/apogee_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/apogee_redled"
-                       android:contentDescription="@string/apogee_voltage_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/apogee_voltage_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/apogee_greenled"
-                       android:text="@string/apogee_voltage_label" />
-
-               <TextView
-                       android:id="@+id/apogee_voltage_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_below="@id/apogee_voltage_label"
-                       android:layout_toRightOf="@id/apogee_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/main_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/main_voltage_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/main_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/main_redled"
-                       android:contentDescription="@string/main_voltage_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/main_voltage_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/main_greenled"
-                       android:text="@string/main_voltage_label" />
-
-               <TextView
-                       android:id="@+id/main_voltage_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_below="@id/main_voltage_label"
-                       android:layout_toRightOf="@id/main_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/logging_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/logging_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/logging_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/logging_redled"
-                       android:contentDescription="@string/logging_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/logging_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/logging_greenled"
-                       android:text="@string/logging_label" />
-
-               <TextView
-                       android:id="@+id/logging_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_below="@id/logging_label"
-                       android:layout_toRightOf="@id/logging_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/gps_locked_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/gps_locked_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/gps_locked_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/gps_locked_redled"
-                       android:contentDescription="@string/gps_locked_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/gps_locked_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/gps_locked_greenled"
-                       android:text="@string/gps_locked_label" />
-
-               <TextView
-                       android:id="@+id/gps_locked_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_below="@id/gps_locked_label"
-                       android:layout_toRightOf="@id/gps_locked_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingTop="5dp" >
-
-               <ImageView
-                       android:id="@+id/gps_ready_redled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:contentDescription="@string/gps_ready_label"
-                       android:src="@drawable/grayled" />
-
-               <ImageView
-                       android:id="@+id/gps_ready_greenled"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/gps_ready_redled"
-                       android:contentDescription="@string/gps_ready_label"
-                       android:paddingRight="5dp"
-                       android:src="@drawable/grayled" />
-
-               <TextView
-                       android:id="@+id/gps_ready_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
-                       android:text="@string/gps_ready_label" />
-
-               <TextView
-                       android:id="@+id/gps_ready_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_below="@id/gps_ready_label"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingLeft="69dp">
-
-               <TextView
-                       android:id="@+id/pad_lat_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:width="100sp"
-                       android:paddingRight="10sp"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
-                       android:text="@string/pad_lat_label" />
-
-               <TextView
-                       android:id="@+id/pad_lat_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/pad_lat_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingLeft="69dp">
-
-               <TextView
-                       android:id="@+id/pad_lon_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:width="100sp"
-                       android:paddingRight="10sp"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
-                       android:text="@string/pad_lon_label" />
-
-               <TextView
-                       android:id="@+id/pad_lon_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/pad_lon_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-       <RelativeLayout
-               android:layout_width="wrap_content"
-               android:layout_height="wrap_content"
-               android:paddingLeft="69dp">
-
-               <TextView
-                       android:id="@+id/pad_alt_label"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:width="100sp"
-                       android:paddingRight="10sp"
-                       android:layout_toRightOf="@id/gps_ready_greenled"
-                       android:text="@string/pad_alt_label" />
-
-               <TextView
-                       android:id="@+id/pad_alt_value"
-                       android:layout_width="wrap_content"
-                       android:layout_height="wrap_content"
-                       android:layout_toRightOf="@id/pad_alt_label"
-                       android:text=""
-                       android:textAppearance="?android:attr/textAppearanceSmall" />
-       </RelativeLayout>
-
-</LinearLayout>
\ No newline at end of file
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+
+  <TableLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:stretchColumns="2,3"
+      android:layout_weight="0"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content">
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+      
+      <ImageView
+         android:id="@+id/battery_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/battery_voltage_label"
+         android:src="@drawable/grayled"
+         />
+
+      <ImageView
+         android:id="@+id/battery_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/battery_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/battery_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/battery_voltage_label" />
+
+      <TextView
+         android:id="@+id/battery_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall"
+         />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/receiver_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <ImageView
+         android:id="@+id/receiver_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/receiver_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/receiver_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/receiver_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/receiver_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/receiver_voltage_label" />
+
+      <TextView
+         android:id="@+id/receiver_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/logging_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/logging_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/logging_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/logging_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/logging_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/logging_label" />
+
+      <TextView
+         android:id="@+id/logging_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_below="@id/logging_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/gps_locked_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_locked_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/gps_locked_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_locked_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/gps_locked_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/gps_locked_label" />
+
+      <TextView
+         android:id="@+id/gps_locked_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_below="@id/gps_locked_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/gps_ready_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_ready_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/gps_ready_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/gps_ready_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/gps_ready_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/gps_ready_label" />
+
+      <TextView
+         android:id="@+id/gps_ready_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_below="@id/gps_ready_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/apogee_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <ImageView
+         android:id="@+id/apogee_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/apogee_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/apogee_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/apogee_voltage_label" />
+
+      <TextView
+         android:id="@+id/apogee_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/main_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/main_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/main_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/main_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/main_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/main_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/main_voltage_label" />
+
+      <TextView
+         android:id="@+id/main_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_a_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_a_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_a_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_a_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_a_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_a_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_a_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_a_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_b_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_b_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_b_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_b_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_b_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_b_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_b_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_b_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_c_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_c_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_c_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_c_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_c_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_c_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_c_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_c_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:id="@+id/ignite_d_row"
+       android:visibility="gone"
+       android:layout_gravity="center"
+       android:layout_weight="1"
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <ImageView
+         android:id="@+id/ignite_d_redled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_d_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <ImageView
+         android:id="@+id/ignite_d_greenled"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:contentDescription="@string/ignite_d_voltage_label"
+         android:src="@drawable/grayled" />
+
+      <TextView
+         android:id="@+id/ignite_d_voltage_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/ignite_d_voltage_label" />
+
+      <TextView
+         android:id="@+id/ignite_d_voltage_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_column="2"
+         android:text="@string/receiver_latitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_column="2"
+         android:text="@string/receiver_longitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+
+    <TableRow
+       android:padding="2dip"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_alt_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_column="2"
+         android:text="@string/receiver_altitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_alt_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </TableRow>
+  </TableLayout>
+</LinearLayout>
diff --git a/altosdroid/res/layout/tab_recover.xml b/altosdroid/res/layout/tab_recover.xml
new file mode 100644 (file)
index 0000000..201f45e
--- /dev/null
@@ -0,0 +1,251 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright © 2013 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.
+-->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical" >
+
+  <LinearLayout
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_weight="0"
+      android:layout_width="fill_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical" >
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <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/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content" >
+
+      <TextView
+         android:id="@+id/direction_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/direction_label" />
+
+      <TextView
+         android:id="@+id/direction_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@+id/direction_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/distance_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/distance_label" />
+
+      <TextView
+         android:id="@+id/distance_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@+id/distance_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/target_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/target_latitude_label" />
+
+      <TextView
+         android:id="@+id/target_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/target_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/target_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/target_longitude_label" />
+
+      <TextView
+         android:id="@+id/target_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/target_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lat_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/receiver_latitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lat_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/receiver_lat_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/receiver_lon_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/receiver_longitude_label" />
+
+      <TextView
+         android:id="@+id/receiver_lon_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/receiver_lon_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/max_height_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/max_height_label" />
+
+      <TextView
+         android:id="@+id/max_height_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/max_height_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/max_speed_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/max_speed_label" />
+
+      <TextView
+         android:id="@+id/max_speed_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/max_speed_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+
+    <RelativeLayout
+       android:layout_gravity="fill"
+       android:layout_weight="1"
+       android:layout_width="wrap_content"
+       android:layout_height="wrap_content">
+
+      <TextView
+         android:id="@+id/max_accel_label"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:text="@string/max_accel_label" />
+
+      <TextView
+         android:id="@+id/max_accel_value"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentRight="true"
+         android:layout_below="@id/max_accel_label"
+         android:text=""
+         android:textAppearance="?android:attr/textAppearanceSmall" />
+    </RelativeLayout>
+  </LinearLayout>
+</LinearLayout>
index 3bd5a54e2bb4b6d6dd0abcb81f5fb39d94e7954e..7e08c80333c30a3c8958f52533e4cb8d9a68fe00 100644 (file)
@@ -1,25 +1,27 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- 
+  Copyright © 2015 Keith Packard <keithp@keithp.com>
 
-     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
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; version 2 of the License.
 
-          http://www.apache.org/licenses/LICENSE-2.0
+  This program is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+  General Public License for more details.
 
-     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.
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 -->
 <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/quit"
-          android:icon="@android:drawable/ic_menu_close_clear_cancel"
-          android:title="@string/quit" />
+    <item android:id="@+id/disconnect"
+         android:icon="@android:drawable/ic_notification_clear_all"
+         android:title="@string/disconnect_device" />
     <item android:id="@+id/select_freq"
           android:icon="@android:drawable/ic_menu_preferences"
           android:title="@string/select_freq" />
     <item android:id="@+id/change_units"
          android:icon="@android:drawable/ic_menu_view"
          android:title="@string/change_units" />
+    <item android:id="@+id/preload_maps"
+         android:icon="@android:drawable/ic_menu_mapmode"
+         android:title="@string/preload_maps" />
+    <item android:id="@+id/map_type"
+         android:icon="@android:drawable/ic_menu_mapmode"
+         android:title="@string/map_type" />
+    <item android:id="@+id/map_source"
+         android:icon="@android:drawable/ic_menu_mapmode"
+         android:title="@string/map_source" />
+    <item android:id="@+id/select_tracker"
+         android:icon="@android:drawable/ic_menu_view"
+         android:title="@string/select_tracker"/>
+    <item android:id="@+id/delete_track"
+         android:icon="@android:drawable/ic_notification_clear_all"
+         android:title="@string/delete_track"/>
+    <item android:id="@+id/quit"
+          android:icon="@android:drawable/ic_menu_close_clear_cancel"
+          android:title="@string/quit" />
 </menu>
diff --git a/altosdroid/res/values/Colors.xml b/altosdroid/res/values/Colors.xml
new file mode 100644 (file)
index 0000000..055c6df
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+        Copyright © 2015 Keith Packard <keithp@keithp.com>
+
+        This program is free software; you can redistribute it and/or modify
+        it under the terms of the GNU General Public License as published by
+        the Free Software Foundation; version 2 of the License.
+
+        This program is distributed in the hope that it will be useful, but
+        WITHOUT ANY WARRANTY; without even the implied warranty of
+        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+        General Public License for more details.
+
+        You should have received a copy of the GNU General Public License along
+        with this program; if not, write to the Free Software Foundation, Inc.,
+        59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+-->
+<resources>
+  <color name="old_color">#ffff4040</color>
+</resources>
diff --git a/altosdroid/res/values/CustomTheme.xml b/altosdroid/res/values/CustomTheme.xml
new file mode 100644 (file)
index 0000000..6c701ea
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+  <style name="CustomTheme" parent="android:Theme.Holo">
+  </style>
+</resources>
index 0cc9934937ad07634cbf97a75c19b5d51020f75d..e7014fc934db5f76555d064c2f9ff6ce2cf65083 100644 (file)
 
        <!-- Options Menu -->
        <string name="connect_device">Connect a device</string>
+       <string name="disconnect_device">Disconnect device</string>
        <string name="quit">Quit</string>
        <string name="select_freq">Select radio frequency</string>
        <string name="select_rate">Select data rate</string>
        <string name="change_units">Change units</string>
+       <string name="preload_maps">Load Maps</string>
+       <string name="select_tracker">Select Tracker</string>
+       <string name="delete_track">Delete Track</string>
+       <string name="map_type">Map Type</string>
+       <string name="map_source">Toggle Online/Offline maps</string>
+
+       <!-- MapTypeActivity -->
+       <string name="map_type">Map Type</string>
 
        <!-- DeviceListActivity -->
        <string name="scanning">scanning for devices…</string>
@@ -60,6 +69,7 @@
        <string name="speed_label">Speed</string>
        <string name="accel_label">Acceleration</string>
        <string name="bearing_label">Bearing</string>
+       <string name="direction_label">Direction</string>
        <string name="elevation_label">Elevation</string>
        <string name="range_label">Range</string>
        <string name="distance_label">Distance</string>
        <string name="max_height_label">Max Height</string>
        <string name="max_speed_label">Max Speed</string>
        <string name="max_accel_label">Max Accel</string>
-       <string name="battery_voltage_label">Battery Voltage</string>
-       <string name="apogee_voltage_label">Apogee Igniter Voltage</string>
-       <string name="main_voltage_label">Main Igniter Voltage</string>
-       <string name="logging_label">On-board Data Logging</string>
+       <string name="battery_voltage_label">Battery</string>
+       <string name="receiver_voltage_label">Receiver Battery</string>
+       <string name="apogee_voltage_label">Apogee Igniter</string>
+       <string name="main_voltage_label">Main Igniter</string>
+       <string name="ignite_a_voltage_label">Igniter A</string>
+       <string name="ignite_b_voltage_label">Igniter B</string>
+       <string name="ignite_c_voltage_label">Igniter C</string>
+       <string name="ignite_d_voltage_label">Igniter D</string>
+       <string name="logging_label">Data Logging</string>
        <string name="gps_locked_label">GPS Locked</string>
        <string name="gps_ready_label">GPS Ready</string>
        <string name="latitude_label">Latitude</string>
        <string name="target_longitude_label">Tar Lon</string>
        <string name="receiver_latitude_label">My Lat</string>
        <string name="receiver_longitude_label">My Lon</string>
-       <string name="pad_lat_label">Pad Lat</string>
-       <string name="pad_lon_label">Pad Lon</string>
-       <string name="pad_alt_label">Pad Alt</string>
+       <string name="receiver_altitude_label">My Alt</string>
+
+       <!-- Map preload -->
+       <string name="preload_site_label">Known Launch Sites</string>
+       <string name="preload_latitude_label">Latitude</string>
+       <string name="preload_longitude_label">Longitude</string>
 
+       <string name="preload_types">Map Types</string>
+       <string name="preload_hybrid">Hybrid</string>
+       <string name="preload_satellite">Satellite</string>
+       <string name="preload_roadmap">Roadmap</string>
+       <string name="preload_terrain">Terrain</string>
+       <string name="preload_min_zoom">Minimum Zoom</string>
+       <string name="preload_max_zoom">Maximum Zoom</string>
+       <string name="preload_radius">Radius</string>
+       
+       <string name="preload_load">Load Map</string>
 </resources>
diff --git a/altosdroid/res/xml/device_filter.xml b/altosdroid/res/xml/device_filter.xml
new file mode 100644 (file)
index 0000000..84b09c0
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+  <usb-device vendor-id="65534" />
+  <usb-device vendor-id="1027" />
+</resources>
index 3740f55d7f83a44d49cf7eabe08a150a4222fef6..976e64bb0e195c14ff271f84513198637ac0fb18 100644 (file)
@@ -29,236 +29,191 @@ import android.bluetooth.BluetoothSocket;
 //import android.os.Bundle;
 import android.os.Handler;
 //import android.os.Message;
-import android.util.Log;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
-public class AltosBluetooth extends AltosLink {
-
-       // Debugging
-       private static final String TAG = "AltosBluetooth";
-       private static final boolean D = true;
+public class AltosBluetooth extends AltosDroidLink {
 
        private ConnectThread    connect_thread = null;
-       private Thread           input_thread   = null;
-
-       private Handler          handler;
 
-       private BluetoothAdapter adapter;
-       private BluetoothDevice  device;
+       private BluetoothDevice  device;
        private BluetoothSocket  socket;
        private InputStream      input;
        private OutputStream     output;
+       private boolean          pause;
 
        // Constructor
-       public AltosBluetooth(BluetoothDevice in_device, Handler in_handler) {
-//             set_debug(D);
-               adapter = BluetoothAdapter.getDefaultAdapter();
-               device = in_device;
-               handler = in_handler;
+       public AltosBluetooth(BluetoothDevice device, Handler handler, boolean pause) {
+               super(handler);
+               this.device = device;
+               this.handler = handler;
+               this.pause = pause;
 
-               connect_thread = new ConnectThread(device);
+               connect_thread = new ConnectThread();
                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;
+       void connected() {
+               if (closed()) {
+                       AltosDebug.debug("connected after closed");
+                       return;
                }
 
-               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();
-
+               AltosDebug.check_ui("connected\n");
+               try {
+                       synchronized(this) {
+                               if (socket != null) {
                                        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;
+                                       super.connected();
                                }
+                       }
+               } catch (InterruptedException ie) {
+                       connect_failed();
+               } catch (IOException io) {
+                       connect_failed();
+               }
+       }
 
-                               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();
+       private void connect_failed() {
+               if (closed()) {
+                       AltosDebug.debug("connect_failed after closed");
+                       return;
+               }
 
-                               // Notify other waiting threads, now that we're connected
-                               AltosBluetooth.this.notifyAll();
+               close_device();
+               input = null;
+               output = null;
+               handler.obtainMessage(TelemetryService.MSG_CONNECT_FAILED, this).sendToTarget();
+               AltosDebug.error("ConnectThread: Failed to establish connection");
+       }
 
-                               // Reset the ConnectThread because we're done
-                               connect_thread = null;
+       void close_device() {
+               BluetoothSocket tmp_socket;
 
-                               if (D) Log.d(TAG, "ConnectThread: Connect completed");
-                       }
+               synchronized(this) {
+                       tmp_socket = socket;
+                       socket = null;
                }
 
-               public void cancel() {
+               if (tmp_socket != null) {
                        try {
-                               if (socket != null)
-                                       socket.close();
+                               tmp_socket.close();
                        } catch (IOException e) {
-                               if (D) Log.e(TAG, "ConnectThread: close() of connect socket failed", e);
+                               AltosDebug.error("close_socket failed");
                        }
                }
        }
 
-       public double frequency() {
-               return frequency;
-       }
-
-       public int telemetry_rate() {
-               return telemetry_rate;
-       }
-
-       public void save_frequency() {
-               AltosPreferences.set_frequency(0, frequency);
+       public void close() {
+               super.close();
+               input = null;
+               output = null;
        }
 
-       public void save_telemetry_rate() {
-               AltosPreferences.set_telemetry_rate(0, telemetry_rate);
-       }
+       private final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
 
-       private synchronized void wait_connected() throws InterruptedException, IOException {
-               if (input == null) {
-                       if (D) Log.d(TAG, "wait_connected...");
-                       wait();
-                       if (D) Log.d(TAG, "wait_connected done");
-                       if (input == null) throw new IOException();
-               }
-       }
+       private void create_socket(BluetoothDevice  device) {
 
-       private void connection_lost() {
-               if (D) Log.e(TAG, "Connection lost during I/O");
-               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED).sendToTarget();
-       }
+               BluetoothSocket tmp_socket = null;
 
-       public void print(String data) {
-               byte[] bytes = data.getBytes();
-               if (D) Log.d(TAG, "print(): begin");
+               AltosDebug.check_ui("create_socket\n");
                try {
-                       wait_connected();
-                       output.write(bytes);
-                       if (D) Log.d(TAG, "print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
+                       tmp_socket = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
                } catch (IOException e) {
-                       connection_lost();
-               } catch (InterruptedException e) {
-                       connection_lost();
+                       e.printStackTrace();
                }
-       }
-
-       public void putchar(byte c) {
-               byte[] bytes = { c };
-               if (D) Log.d(TAG, "print(): begin");
-               try {
-                       wait_connected();
-                       output.write(bytes);
-                       if (D) Log.d(TAG, "print(): Wrote byte: '" + c + "'");
-               } catch (IOException e) {
-                       connection_lost();
-               } catch (InterruptedException e) {
-                       connection_lost();
+               if (socket != null) {
+                       AltosDebug.debug("Socket already allocated %s", socket.toString());
+                       close_device();
+               }
+               synchronized (this) {
+                       socket = tmp_socket;
                }
        }
 
-       private static final int buffer_size = 1024;
+       private class ConnectThread extends Thread {
 
-       private byte[] buffer = new byte[buffer_size];
-       private int buffer_len = 0;
-       private int buffer_off = 0;
+               public void run() {
+                       AltosDebug.debug("ConnectThread: BEGIN (pause %b)", pause);
+                       setName("ConnectThread");
 
-       public int getchar() {
-               while (buffer_off == buffer_len) {
+                       if (pause) {
+                               try {
+                                       Thread.sleep(4000);
+                               } catch (InterruptedException e) {
+                               }
+                       }
+
+                       create_socket(device);
+                       // Always cancel discovery because it will slow down a connection
                        try {
-                               wait_connected();
-                               buffer_len = input.read(buffer);
-                               buffer_off = 0;
-                       } catch (IOException e) {
-                               connection_lost();
-                               return AltosLink.ERROR;
-                       } catch (java.lang.InterruptedException e) {
-                               connection_lost();
-                               return AltosLink.ERROR;
+                               BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
+                       } catch (Exception e) {
+                               AltosDebug.debug("cancelDiscovery exception %s", e.toString());
                        }
-               }
-               return buffer[buffer_off++];
-       }
 
-       public void close() {
-               if (D) Log.d(TAG, "close(): begin");
-               synchronized(this) {
-                       if (D) Log.d(TAG, "close(): synched");
+                       BluetoothSocket local_socket = null;
 
-                       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");
+                       synchronized (AltosBluetooth.this) {
+                               if (!closed())
+                                       local_socket = socket;
                        }
-                       if (input_thread != null) {
-                               if (D) Log.d(TAG, "close(): stopping input_thread");
+
+                       if (local_socket != null) {
                                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;
+                                       // Make a connection to the BluetoothSocket
+                                       // This is a blocking call and will only return on a
+                                       // successful connection or an exception
+                                       local_socket.connect();
+                               } catch (Exception e) {
+                                       AltosDebug.debug("Connect exception %s", e.toString());
+                                       try {
+                                               local_socket.close();
+                                       } catch (Exception ce) {
+                                               AltosDebug.debug("Close exception %s", ce.toString());
+                                       }
+                                       local_socket = null;
+                               }
+                       }
+
+                       if (local_socket != null) {
+                               connected();
+                       } else {
+                               connect_failed();
                        }
-                       input = null;
-                       output = null;
-                       notifyAll();
+
+                       AltosDebug.debug("ConnectThread: completed");
                }
        }
 
+       private synchronized void wait_connected() throws InterruptedException, IOException {
+               AltosDebug.check_ui("wait_connected\n");
+               if (input == null && socket != null) {
+                       AltosDebug.debug("wait_connected...");
+                       wait();
+                       AltosDebug.debug("wait_connected done");
+               }
+               if (socket == null)
+                       throw new IOException();
+       }
 
-       // 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);
+       int write(byte[] buffer, int len) {
+               try {
+                       output.write(buffer, 0, len);
+               } catch (IOException ie) {
+                       return -1;
+               }
+               return len;
        }
 
-       //public void flush_output() { super.flush_output(); }
+       int read(byte[] buffer, int len) {
+               try {
+                       return input.read(buffer, 0, len);
+               } catch (IOException ie) {
+                       return -1;
+               }
+       }
 
        // Stubs of required methods when extending AltosLink
        public boolean can_cancel_reply()   { return false; }
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDebug.java
new file mode 100644 (file)
index 0000000..db63d81
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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.util.Arrays;
+import java.io.*;
+import java.lang.*;
+
+import org.altusmetrum.altoslib_8.*;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+import android.content.*;
+import android.util.Log;
+import android.os.*;
+import android.content.pm.*;
+
+public class AltosDebug {
+       // Debugging
+       static final String TAG = "AltosDroid";
+
+       static boolean  D = true;
+
+       static void init(Context context) {
+               ApplicationInfo app_info = context.getApplicationInfo();
+
+               if ((app_info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
+                       Log.d(TAG, "Enable debugging\n");
+                       D = true;
+               } else {
+                       Log.d(TAG, "Disable debugging\n");
+                       D = false;
+               }
+       }
+
+
+       static void info(String format, Object ... arguments) {
+               Log.i(TAG, String.format(format, arguments));
+       }
+
+       static void debug(String format, Object ... arguments) {
+               if (D)
+                       Log.d(TAG, String.format(format, arguments));
+       }
+
+       static void error(String format, Object ... arguments) {
+               Log.e(TAG, String.format(format, arguments));
+       }
+
+       static void check_ui(String format, Object ... arguments) {
+               if (Looper.myLooper() == Looper.getMainLooper()) {
+                       Log.e(TAG, String.format("ON UI THREAD " + format, arguments));
+                       for (StackTraceElement el : Thread.currentThread().getStackTrace())
+                               Log.e(TAG, "\t" + el.toString() + "\n");
+               }
+       }
+}
index 53963f25a2c5ac27a5599c12ded0ad7e71d5cdb9..3a07212a226312995d2d939a4f5ca039b1428a86 100644 (file)
 package org.altusmetrum.AltosDroid;
 
 import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.Timer;
-import java.util.TimerTask;
+import java.text.*;
+import java.util.*;
+import java.io.*;
 
 import android.app.Activity;
+import android.app.PendingIntent;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.content.Intent;
@@ -36,28 +37,26 @@ import android.os.Handler;
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
+import android.content.res.Resources;
 import android.support.v4.app.FragmentActivity;
 import android.support.v4.app.FragmentManager;
 import android.util.DisplayMetrics;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.Window;
-import android.view.View;
-import android.widget.TabHost;
-import android.widget.TextView;
-import android.widget.RelativeLayout;
-import android.widget.Toast;
+import android.view.*;
+import android.widget.*;
 import android.app.AlertDialog;
 import android.location.Location;
+import android.hardware.usb.*;
+import android.graphics.*;
+import android.graphics.drawable.*;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
-       // Debugging
-       static final String TAG = "AltosDroid";
-       static final boolean D = true;
+
+       // Actions sent to the telemetry server at startup time
+
+       public static final String ACTION_BLUETOOTH = "org.altusmetrum.AltosDroid.BLUETOOTH";
+       public static final String ACTION_USB = "org.altusmetrum.AltosDroid.USB";
 
        // Message types received by our Handler
 
@@ -67,14 +66,15 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
        // Intent request codes
        public static final int REQUEST_CONNECT_DEVICE = 1;
        public static final int REQUEST_ENABLE_BT      = 2;
+       public static final int REQUEST_PRELOAD_MAPS   = 3;
+       public static final int REQUEST_MAP_TYPE       = 4;
+
+       public int map_type = AltosMap.maptype_hybrid;
 
        public static FragmentManager   fm;
 
        private BluetoothAdapter mBluetoothAdapter = null;
 
-       // Layout Views
-       private TextView mTitle;
-
        // Flight state values
        private TextView mCallsignView;
        private TextView mRSSIView;
@@ -83,6 +83,14 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
        private RelativeLayout mStateLayout;
        private TextView mStateView;
        private TextView mAgeView;
+       private boolean  mAgeViewOld;
+       private int mAgeNewColor;
+       private int mAgeOldColor;
+
+       public static final String      tab_pad_name = "pad";
+       public static final String      tab_flight_name = "flight";
+       public static final String      tab_recover_name = "recover";
+       public static final String      tab_map_name = "map";
 
        // field to display the version at the bottom of the screen
        private TextView mVersion;
@@ -100,6 +108,11 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
        // Timer and Saved flight state for Age calculation
        private Timer timer;
        AltosState saved_state;
+       TelemetryState  telemetry_state;
+       Integer[]       serials;
+
+       UsbDevice       pending_usb_device;
+       boolean         start_with_usb;
 
        // Service
        private boolean mIsBound   = false;
@@ -120,17 +133,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
 
                        switch (msg.what) {
                        case MSG_STATE:
-                               if(D) Log.d(TAG, "MSG_STATE");
-                               TelemetryState telemetry_state = (TelemetryState) msg.obj;
-                               if (telemetry_state == null) {
-                                       Log.d(TAG, "telemetry_state null!");
+                               if (msg.obj == null) {
+                                       AltosDebug.debug("telemetry_state null!");
                                        return;
                                }
-
-                               ad.update_state(telemetry_state);
+                               ad.update_state((TelemetryState) msg.obj);
                                break;
                        case MSG_UPDATE_AGE:
-                               if(D) Log.d(TAG, "MSG_UPDATE_AGE");
                                ad.update_age();
                                break;
                        }
@@ -148,6 +157,13 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                        } catch (RemoteException e) {
                                // In this case the service has crashed before we could even do anything with it
                        }
+                       if (pending_usb_device != null) {
+                               try {
+                                       mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, pending_usb_device));
+                                       pending_usb_device = null;
+                               } catch (RemoteException e) {
+                               }
+                       }
                }
 
                public void onServiceDisconnected(ComponentName className) {
@@ -201,17 +217,20 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                                if (telemetry_state.telemetry_rate != AltosLib.ao_telemetry_rate_38400)
                                        str = str.concat(String.format(" %d bps",
                                                                       AltosLib.ao_telemetry_rate_values[telemetry_state.telemetry_rate]));
-                               mTitle.setText(str);
+                               setTitle(str);
                        } else {
-                               mTitle.setText(R.string.title_connected_to);
+                               setTitle(R.string.title_connected_to);
                        }
                        break;
                case TelemetryState.CONNECT_CONNECTING:
-                       mTitle.setText(R.string.title_connecting);
+                       if (telemetry_state.address != null)
+                               setTitle(String.format("Connecting to %s...", telemetry_state.address.name));
+                       else
+                               setTitle("Connecting to something...");
                        break;
-               case TelemetryState.CONNECT_READY:
+               case TelemetryState.CONNECT_DISCONNECTED:
                case TelemetryState.CONNECT_NONE:
-                       mTitle.setText(R.string.title_not_connected);
+                       setTitle(R.string.title_not_connected);
                        break;
                }
        }
@@ -231,21 +250,73 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                }
        }
 
+       int     selected_serial = 0;
+       int     current_serial;
+       long    switch_time;
+
+       void set_switch_time() {
+               switch_time = System.currentTimeMillis();
+               selected_serial = 0;
+       }
+
        boolean registered_units_listener;
 
-       void update_state(TelemetryState telemetry_state) {
+       void update_state(TelemetryState new_telemetry_state) {
+
+               if (new_telemetry_state != null)
+                       telemetry_state = new_telemetry_state;
+
+               if (selected_serial != 0)
+                       current_serial = selected_serial;
+
+               if (current_serial == 0)
+                       current_serial = telemetry_state.latest_serial;
 
                if (!registered_units_listener) {
                        registered_units_listener = true;
                        AltosPreferences.register_units_listener(this);
                }
 
+               serials = telemetry_state.states.keySet().toArray(new Integer[0]);
+               Arrays.sort(serials);
+
                update_title(telemetry_state);
-               update_ui(telemetry_state.state, telemetry_state.location);
-               if (telemetry_state.connect == TelemetryState.CONNECT_CONNECTED)
-                       start_timer();
-               else
-                       stop_timer();
+
+               AltosState      state = null;
+               boolean         aged = true;
+
+               if (telemetry_state.states.containsKey(current_serial)) {
+                       state = telemetry_state.states.get(current_serial);
+                       int age = state_age(state);
+                       if (age < 20)
+                               aged = false;
+                       if (current_serial == selected_serial)
+                               aged = false;
+                       else if (switch_time != 0 && (switch_time - state.received_time) > 0)
+                               aged = true;
+               }
+
+               if (aged) {
+                       AltosState      newest_state = null;
+                       int             newest_age = 0;
+
+                       for (int serial : telemetry_state.states.keySet()) {
+                               AltosState      existing = telemetry_state.states.get(serial);
+                               int             existing_age = state_age(existing);
+
+                               if (newest_state == null || existing_age < newest_age) {
+                                       newest_state = existing;
+                                       newest_age = existing_age;
+                               }
+                       }
+
+                       if (newest_state != null)
+                               state = newest_state;
+               }
+
+               update_ui(telemetry_state, state, telemetry_state.location);
+
+               start_timer();
        }
 
        boolean same_string(String a, String b) {
@@ -260,14 +331,55 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                }
        }
 
-       void update_age() {
-               if (saved_state != null)
-                       mAgeView.setText(String.format("%d", (System.currentTimeMillis() - saved_state.received_time + 500) / 1000));
+
+       private int blend_component(int a, int b, double r, int shift, int mask) {
+               return ((int) (((a >> shift) & mask) * r + ((b >> shift) & mask) * (1 - r)) & mask) << shift;
        }
+       private int blend_color(int a, int b, double r) {
+               return (blend_component(a, b, r, 0, 0xff) |
+                       blend_component(a, b, r, 8, 0xff) |
+                       blend_component(a, b, r, 16, 0xff) |
+                       blend_component(a, b, r, 24, 0xff));
+       }
+
+       int state_age(AltosState state) {
+               return (int) ((System.currentTimeMillis() - state.received_time + 500) / 1000);
+       }
+
+       void set_screen_on(int age) {
+               if (age < 60)
+                       getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+               else
+                       getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+       }
+
+       void update_age() {
+               if (saved_state != null) {
+                       int age = state_age(saved_state);
+
+                       double age_scale = age / 100.0;
+
+                       if (age_scale > 1.0)
+                               age_scale = 1.0;
 
-       void update_ui(AltosState state, Location location) {
+                       mAgeView.setTextColor(blend_color(mAgeOldColor, mAgeNewColor, age_scale));
 
-               Log.d(TAG, "update_ui");
+                       set_screen_on(age);
+
+                       String  text;
+                       if (age < 60)
+                               text = String.format("%ds", age);
+                       else if (age < 60 * 60)
+                               text = String.format("%dm", age / 60);
+                       else if (age < 60 * 60 * 24)
+                               text = String.format("%dh", age / (60 * 60));
+                       else
+                               text = String.format("%dd", age / (24 * 60 * 60));
+                       mAgeView.setText(text);
+               }
+       }
+
+       void update_ui(TelemetryState telem_state, AltosState state, Location location) {
 
                int prev_state = AltosLib.ao_flight_invalid;
 
@@ -277,7 +389,8 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                        prev_state = saved_state.state;
 
                if (state != null) {
-                       Log.d(TAG, String.format("prev state %d new state  %d\n", prev_state, state.state));
+                       set_screen_on(state_age(state));
+
                        if (state.state == AltosLib.ao_flight_stateless) {
                                boolean prev_locked = false;
                                boolean locked = false;
@@ -289,27 +402,23 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                                if (prev_locked != locked) {
                                        String currentTab = mTabHost.getCurrentTabTag();
                                        if (locked) {
-                                               if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
                                        } else {
-                                               if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("pad");
+                                               if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_pad_name);
                                        }
                                }
                        } else {
                                if (prev_state != state.state) {
                                        String currentTab = mTabHost.getCurrentTabTag();
-                                       Log.d(TAG, "switch state");
                                        switch (state.state) {
                                        case AltosLib.ao_flight_boost:
-                                               if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("ascent");
-                                               break;
-                                       case AltosLib.ao_flight_drogue:
-                                               if (currentTab.equals("ascent")) mTabHost.setCurrentTabByTag("descent");
+                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
                                                break;
                                        case AltosLib.ao_flight_landed:
-                                               if (currentTab.equals("descent")) mTabHost.setCurrentTabByTag("landed");
+                                               if (currentTab.equals(tab_flight_name)) mTabHost.setCurrentTabByTag(tab_recover_name);
                                                break;
                                        case AltosLib.ao_flight_stateless:
-                                               if (currentTab.equals("pad")) mTabHost.setCurrentTabByTag("descent");
+                                               if (currentTab.equals(tab_pad_name)) mTabHost.setCurrentTabByTag(tab_flight_name);
                                                break;
                                        }
                                }
@@ -328,22 +437,18 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                        }
 
                        if (saved_state == null || !same_string(saved_state.callsign, state.callsign)) {
-                               Log.d(TAG, "update callsign");
                                mCallsignView.setText(state.callsign);
                        }
                        if (saved_state == null || state.serial != saved_state.serial) {
-                               Log.d(TAG, "update serial");
                                mSerialView.setText(String.format("%d", state.serial));
                        }
                        if (saved_state == null || state.flight != saved_state.flight) {
-                               Log.d(TAG, "update flight");
                                if (state.flight == AltosLib.MISSING)
                                        mFlightView.setText("");
                                else
                                        mFlightView.setText(String.format("%d", state.flight));
                        }
                        if (saved_state == null || state.state != saved_state.state) {
-                               Log.d(TAG, "update state");
                                if (state.state == AltosLib.ao_flight_stateless) {
                                        mStateLayout.setVisibility(View.GONE);
                                } else {
@@ -352,16 +457,15 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                                }
                        }
                        if (saved_state == null || state.rssi != saved_state.rssi) {
-                               Log.d(TAG, "update rssi");
                                mRSSIView.setText(String.format("%d", state.rssi));
                        }
                }
 
                for (AltosDroidTab mTab : mTabs)
-                       mTab.update_ui(state, from_receiver, location, mTab == mTabsAdapter.currentItem());
+                       mTab.update_ui(telem_state, state, from_receiver, location, mTab == mTabsAdapter.currentItem());
 
-               if (state != null)
-                       mAltosVoice.tell(state, from_receiver);
+               if (mAltosVoice != null)
+                       mAltosVoice.tell(telem_state, state, from_receiver, location, (AltosDroidTab) mTabsAdapter.currentItem());
 
                saved_state = state;
        }
@@ -398,26 +502,32 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                return String.format(format, value);
        }
 
+       private View create_tab_view(String label) {
+               LayoutInflater inflater = (LayoutInflater) this.getLayoutInflater();
+               View tab_view = inflater.inflate(R.layout.tab_layout, null);
+               TextView text_view = (TextView) tab_view.findViewById (R.id.tabLabel);
+               text_view.setText(label);
+               return tab_view;
+       }
+
+       public void set_map_source(int source) {
+               for (AltosDroidTab mTab : mTabs)
+                       mTab.set_map_source(source);
+       }
+
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
-               if(D) Log.e(TAG, "+++ ON CREATE +++");
+               AltosDebug.init(this);
+               AltosDebug.debug("+++ 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();
-               }
+               // Initialise preferences
+               AltosDroidPreferences.init(this);
 
                fm = getSupportFragmentManager();
 
                // Set up the window layout
-               requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
                setContentView(R.layout.altosdroid);
-               getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
 
                // Create the Tabs and ViewPager
                mTabHost = (TabHost)findViewById(android.R.id.tabhost);
@@ -428,37 +538,10 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
 
                mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
 
-               mTabsAdapter.addTab(mTabHost.newTabSpec("pad").setIndicator("Pad"), TabPad.class, null);
-               mTabsAdapter.addTab(mTabHost.newTabSpec("ascent").setIndicator("Ascent"), TabAscent.class, null);
-               mTabsAdapter.addTab(mTabHost.newTabSpec("descent").setIndicator("Descent"), TabDescent.class, null);
-               mTabsAdapter.addTab(mTabHost.newTabSpec("landed").setIndicator("Landed"), TabLanded.class, null);
-               mTabsAdapter.addTab(mTabHost.newTabSpec("map").setIndicator("Map"), TabMap.class, null);
-
-
-               // Scale the size of the Tab bar for different screen densities
-               // This probably won't be needed when we start supporting ICS+ tabs.
-               DisplayMetrics metrics = new DisplayMetrics();
-               getWindowManager().getDefaultDisplay().getMetrics(metrics);
-               int density = metrics.densityDpi;
-
-               if (density==DisplayMetrics.DENSITY_XHIGH)
-                       tabHeight = 65;
-               else if (density==DisplayMetrics.DENSITY_HIGH)
-                       tabHeight = 45;
-               else if (density==DisplayMetrics.DENSITY_MEDIUM)
-                       tabHeight = 35;
-               else if (density==DisplayMetrics.DENSITY_LOW)
-                       tabHeight = 25;
-               else
-                       tabHeight = 65;
-
-               for (int i = 0; i < 5; i++)
-                       mTabHost.getTabWidget().getChildAt(i).getLayoutParams().height = tabHeight;
-
-               // 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);
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_pad_name).setIndicator(create_tab_view("Pad")), TabPad.class, null);
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_flight_name).setIndicator(create_tab_view("Flight")), TabFlight.class, null);
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_recover_name).setIndicator(create_tab_view("Recover")), TabRecover.class, null);
+               mTabsAdapter.addTab(mTabHost.newTabSpec(tab_map_name).setIndicator(create_tab_view("Map")), TabMap.class, null);
 
                // Display the Version
                mVersion = (TextView) findViewById(R.id.version);
@@ -473,58 +556,158 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                mStateLayout   = (RelativeLayout) findViewById(R.id.state_container);
                mStateView     = (TextView) findViewById(R.id.state_value);
                mAgeView       = (TextView) findViewById(R.id.age_value);
+               mAgeNewColor   = mAgeView.getTextColors().getDefaultColor();
+               mAgeOldColor   = getResources().getColor(R.color.old_color);
+       }
+
+       private boolean ensureBluetooth() {
+               // 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();
+                       return false;
+               }
+
+               if (!mBluetoothAdapter.isEnabled()) {
+                       Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
+                       startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
+               }
+
+               return true;
+       }
+
+       private boolean check_usb() {
+               UsbDevice       device = AltosUsb.find_device(this, AltosLib.product_basestation);
+
+               if (device != null) {
+                       Intent          i = new Intent(this, AltosDroid.class);
+                       PendingIntent   pi = PendingIntent.getActivity(this, 0, new Intent("hello world", null, this, AltosDroid.class), 0);
 
-               mAltosVoice = new AltosVoice(this);
+                       if (AltosUsb.request_permission(this, device, pi)) {
+                               connectUsb(device);
+                       }
+                       start_with_usb = true;
+                       return true;
+               }
+
+               start_with_usb = false;
+
+               return false;
+       }
+
+       private void noticeIntent(Intent intent) {
+
+               /* Ok, this is pretty convenient.
+                *
+                * When a USB device is plugged in, and our 'hotplug'
+                * intent registration fires, we get an Intent with
+                * EXTRA_DEVICE set.
+                *
+                * When we start up and see a usb device and request
+                * permission to access it, that queues a
+                * PendingIntent, which has the EXTRA_DEVICE added in,
+                * along with the EXTRA_PERMISSION_GRANTED field as
+                * well.
+                *
+                * So, in both cases, we get the device name using the
+                * same call. We check to see if access was granted,
+                * in which case we ignore the device field and do our
+                * usual startup thing.
+                */
+
+               UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+               boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+
+               AltosDebug.debug("intent %s device %s granted %s", intent, device, granted);
+
+               if (!granted)
+                       device = null;
+
+               if (device != null) {
+                       AltosDebug.debug("intent has usb device " + device.toString());
+                       connectUsb(device);
+               } else {
+
+                       /* 'granted' is only false if this intent came
+                        * from the request_permission call and
+                        * permission was denied. In which case, we
+                        * don't want to loop forever...
+                        */
+                       if (granted) {
+                               AltosDebug.debug("check for a USB device at startup");
+                               if (check_usb())
+                                       return;
+                       }
+                       AltosDebug.debug("Starting by looking for bluetooth devices");
+                       if (ensureBluetooth())
+                               return;
+                       finish();
+               }
        }
 
        @Override
        public void onStart() {
                super.onStart();
-               if(D) Log.e(TAG, "++ ON START ++");
+               AltosDebug.debug("++ ON START ++");
+
+               set_switch_time();
+
+               noticeIntent(getIntent());
 
                // Start Telemetry Service
-               startService(new Intent(AltosDroid.this, TelemetryService.class));
+               String  action = start_with_usb ? ACTION_USB : ACTION_BLUETOOTH;
 
-               if (!mBluetoothAdapter.isEnabled()) {
-                       Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
-                       startActivityForResult(enableIntent, AltosDroid.REQUEST_ENABLE_BT);
-               }
+               startService(new Intent(action, null, AltosDroid.this, TelemetryService.class));
 
                doBindService();
 
+               if (mAltosVoice == null)
+                       mAltosVoice = new AltosVoice(this);
+
        }
 
        @Override
-       public synchronized void onResume() {
+       public void onNewIntent(Intent intent) {
+               super.onNewIntent(intent);
+               AltosDebug.debug("onNewIntent");
+               noticeIntent(intent);
+       }
+
+       @Override
+       public void onResume() {
                super.onResume();
-               if(D) Log.e(TAG, "+ ON RESUME +");
+               AltosDebug.debug("+ ON RESUME +");
        }
 
        @Override
-       public synchronized void onPause() {
+       public void onPause() {
                super.onPause();
-               if(D) Log.e(TAG, "- ON PAUSE -");
+               AltosDebug.debug("- ON PAUSE -");
        }
 
        @Override
        public void onStop() {
                super.onStop();
-               if(D) Log.e(TAG, "-- ON STOP --");
-
-               doUnbindService();
+               AltosDebug.debug("-- ON STOP --");
        }
 
        @Override
        public void onDestroy() {
                super.onDestroy();
-               if(D) Log.e(TAG, "--- ON DESTROY ---");
+               AltosDebug.debug("--- ON DESTROY ---");
 
-               if (mAltosVoice != null) mAltosVoice.stop();
+               doUnbindService();
+               if (mAltosVoice != null) {
+                       mAltosVoice.stop();
+                       mAltosVoice = null;
+               }
                stop_timer();
        }
 
-       public void onActivityResult(int requestCode, int resultCode, Intent data) {
-               if(D) Log.d(TAG, "onActivityResult " + resultCode);
+       protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+               AltosDebug.debug("onActivityResult " + resultCode);
                switch (requestCode) {
                case REQUEST_CONNECT_DEVICE:
                        // When DeviceListActivity returns with a device to connect to
@@ -539,28 +722,64 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                                //setupChat();
                        } else {
                                // User did not enable Bluetooth or an error occured
-                               Log.e(TAG, "BT not enabled");
+                               AltosDebug.error("BT not enabled");
                                stopService(new Intent(AltosDroid.this, TelemetryService.class));
                                Toast.makeText(this, R.string.bt_not_enabled, Toast.LENGTH_SHORT).show();
                                finish();
                        }
                        break;
+               case REQUEST_MAP_TYPE:
+                       if (resultCode == Activity.RESULT_OK)
+                               set_map_type(data);
+                       break;
                }
        }
 
-       private void connectDevice(String address) {
+       private void connectUsb(UsbDevice device) {
+               if (mService == null)
+                       pending_usb_device = device;
+               else {
+                       // Attempt to connect to the device
+                       try {
+                               mService.send(Message.obtain(null, TelemetryService.MSG_OPEN_USB, device));
+                               AltosDebug.debug("Sent OPEN_USB message");
+                       } catch (RemoteException e) {
+                               AltosDebug.debug("connect device message failed");
+                       }
+               }
+       }
+
+       private void connectDevice(Intent data) {
                // Attempt to connect to the device
                try {
-                       if (D) Log.d(TAG, "Connecting to " + address);
-                       mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, address));
+                       String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
+                       String name = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_NAME);
+
+                       AltosDebug.debug("Connecting to " + address + " " + name);
+                       DeviceAddress   a = new DeviceAddress(address, name);
+                       mService.send(Message.obtain(null, TelemetryService.MSG_CONNECT, a));
+                       AltosDebug.debug("Sent connecting message");
                } catch (RemoteException e) {
+                       AltosDebug.debug("connect device message failed");
                }
        }
 
-       private void connectDevice(Intent data) {
-               // Get the device MAC address
-               String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
-               connectDevice(address);
+       private void disconnectDevice() {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_DISCONNECT, null));
+               } catch (RemoteException e) {
+               }
+       }
+
+       private void set_map_type(Intent data) {
+               int type = data.getIntExtra(MapTypeActivity.EXTRA_MAP_TYPE, -1);
+
+               AltosDebug.debug("intent set_map_type %d\n", type);
+               if (type != -1) {
+                       map_type = type;
+                       for (AltosDroidTab mTab : mTabs)
+                               mTab.set_map_type(map_type);
+               }
        }
 
        @Override
@@ -573,20 +792,22 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
        void setFrequency(double freq) {
                try {
                        mService.send(Message.obtain(null, TelemetryService.MSG_SETFREQUENCY, freq));
+                       set_switch_time();
                } catch (RemoteException e) {
                }
        }
 
        void setFrequency(String freq) {
                try {
-                       setFrequency (Double.parseDouble(freq.substring(11, 17)));
-               } catch (NumberFormatException e) {
+                       setFrequency (AltosParse.parse_double_net(freq.substring(11, 17)));
+               } catch (ParseException e) {
                }
        }
 
        void setBaud(int baud) {
                try {
                        mService.send(Message.obtain(null, TelemetryService.MSG_SETBAUD, baud));
+                       set_switch_time();
                } catch (RemoteException e) {
                }
        }
@@ -611,18 +832,77 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                }
        }
 
+       void select_tracker(int serial) {
+               int i;
+
+               AltosDebug.debug("select tracker %d\n", serial);
+
+               if (serial == selected_serial) {
+                       AltosDebug.debug("%d already selected\n", serial);
+                       return;
+               }
+
+               if (serial != 0) {
+                       for (i = 0; i < serials.length; i++)
+                               if (serials[i] == serial)
+                                       break;
+
+                       if (i == serials.length) {
+                               AltosDebug.debug("attempt to select unknown tracker %d\n", serial);
+                               return;
+                       }
+               }
+
+               current_serial = selected_serial = serial;
+               update_state(null);
+       }
+
+       void touch_trackers(Integer[] serials) {
+               AlertDialog.Builder builder_tracker = new AlertDialog.Builder(this);
+               builder_tracker.setTitle("Select Tracker");
+               final String[] trackers = new String[serials.length + 1];
+               trackers[0] = "Auto";
+               for (int i = 0; i < serials.length; i++)
+                       trackers[i+1] = String.format("%d", serials[i]);
+               builder_tracker.setItems(trackers,
+                                        new DialogInterface.OnClickListener() {
+                                                public void onClick(DialogInterface dialog, int item) {
+                                                        if (item == 0)
+                                                                select_tracker(0);
+                                                        else
+                                                                select_tracker(Integer.parseInt(trackers[item]));
+                                                }
+                                        });
+               AlertDialog alert_tracker = builder_tracker.create();
+               alert_tracker.show();
+       }
+
+       void delete_track(int serial) {
+               try {
+                       mService.send(Message.obtain(null, TelemetryService.MSG_DELETE_SERIAL, (Integer) serial));
+               } catch (Exception ex) {
+               }
+       }
+
        @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);
+                       if (ensureBluetooth()) {
+                               // 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.disconnect:
+                       /* Disconnect the device
+                        */
+                       disconnectDevice();
                        return true;
                case R.id.quit:
-                       Log.d(TAG, "R.id.quit");
-                       stopService(new Intent(AltosDroid.this, TelemetryService.class));
+                       AltosDebug.debug("R.id.quit");
+                       disconnectDevice();
                        finish();
                        return true;
                case R.id.select_freq:
@@ -676,8 +956,92 @@ public class AltosDroid extends FragmentActivity implements AltosUnitsListener {
                        boolean imperial = AltosPreferences.imperial_units();
                        AltosPreferences.set_imperial_units(!imperial);
                        return true;
+               case R.id.preload_maps:
+                       serverIntent = new Intent(this, PreloadMapActivity.class);
+                       startActivityForResult(serverIntent, REQUEST_PRELOAD_MAPS);
+                       return true;
+               case R.id.map_type:
+                       serverIntent = new Intent(this, MapTypeActivity.class);
+                       startActivityForResult(serverIntent, REQUEST_MAP_TYPE);
+                       return true;
+               case R.id.map_source:
+                       int source = AltosDroidPreferences.map_source();
+                       int new_source = source == AltosDroidPreferences.MAP_SOURCE_ONLINE ? AltosDroidPreferences.MAP_SOURCE_OFFLINE : AltosDroidPreferences.MAP_SOURCE_ONLINE;
+                       AltosDroidPreferences.set_map_source(new_source);
+                       set_map_source(new_source);
+                       return true;
+               case R.id.select_tracker:
+                       if (serials != null) {
+                               String[] trackers = new String[serials.length+1];
+                               trackers[0] = "Auto";
+                               for (int i = 0; i < serials.length; i++)
+                                       trackers[i+1] = String.format("%d", serials[i]);
+                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+                               builder_serial.setTitle("Select a tracker");
+                               builder_serial.setItems(trackers,
+                                                       new DialogInterface.OnClickListener() {
+                                                               public void onClick(DialogInterface dialog, int item) {
+                                                                       if (item == 0)
+                                                                               select_tracker(0);
+                                                                       else
+                                                                               select_tracker(serials[item-1]);
+                                                               }
+                                                       });
+                               AlertDialog alert_serial = builder_serial.create();
+                               alert_serial.show();
+
+                       }
+                       return true;
+               case R.id.delete_track:
+                       if (serials != null) {
+                               String[] trackers = new String[serials.length];
+                               for (int i = 0; i < serials.length; i++)
+                                       trackers[i] = String.format("%d", serials[i]);
+                               AlertDialog.Builder builder_serial = new AlertDialog.Builder(this);
+                               builder_serial.setTitle("Delete a track");
+                               builder_serial.setItems(trackers,
+                                                       new DialogInterface.OnClickListener() {
+                                                               public void onClick(DialogInterface dialog, int item) {
+                                                                       delete_track(serials[item]);
+                                                               }
+                                                       });
+                               AlertDialog alert_serial = builder_serial.create();
+                               alert_serial.show();
+
+                       }
+                       return true;
                }
                return false;
        }
 
+       static String direction(AltosGreatCircle from_receiver,
+                            Location receiver) {
+               if (from_receiver == null)
+                       return null;
+
+               if (receiver == null)
+                       return null;
+
+               if (!receiver.hasBearing())
+                       return null;
+
+               float   bearing = receiver.getBearing();
+               float   heading = (float) from_receiver.bearing - bearing;
+
+               while (heading <= -180.0f)
+                       heading += 360.0f;
+               while (heading > 180.0f)
+                       heading -= 360.0f;
+
+               int iheading = (int) (heading + 0.5f);
+
+               if (-1 < iheading && iheading < 1)
+                       return "ahead";
+               else if (iheading < -179 || 179 < iheading)
+                       return "backwards";
+               else if (iheading < 0)
+                       return String.format("left %d°", -iheading);
+               else
+                       return String.format("right %d°", iheading);
+       }
 }
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidLink.java
new file mode 100644 (file)
index 0000000..7cbba79
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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.os.Handler;
+
+import org.altusmetrum.altoslib_8.*;
+
+public abstract class AltosDroidLink extends AltosLink {
+
+       Handler         handler;
+
+       Thread          input_thread   = null;
+
+       public double frequency() {
+               return frequency;
+       }
+
+       public int telemetry_rate() {
+               return telemetry_rate;
+       }
+
+       public void save_frequency() {
+               AltosPreferences.set_frequency(0, frequency);
+       }
+
+       public void save_telemetry_rate() {
+               AltosPreferences.set_telemetry_rate(0, telemetry_rate);
+       }
+
+       Object closed_lock = new Object();
+       boolean closing = false;
+       boolean closed = false;
+
+       public boolean closed() {
+               synchronized(closed_lock) {
+                       return closing;
+               }
+       }
+
+       void connected() throws InterruptedException {
+               input_thread = new Thread(this);
+               input_thread.start();
+
+               // Configure the newly connected device for telemetry
+               print("~\nE 0\n");
+               set_monitor(false);
+               AltosDebug.debug("ConnectThread: connected");
+
+               /* Let TelemetryService know we're connected
+                */
+               handler.obtainMessage(TelemetryService.MSG_CONNECTED, this).sendToTarget();
+
+               /* Notify other waiting threads that we're connected now
+                */
+               notifyAll();
+       }
+
+       public void closing() {
+               synchronized(closed_lock) {
+                       AltosDebug.debug("Marked closing true");
+                       closing = true;
+               }
+       }
+
+       private boolean actually_closed() {
+               synchronized(closed_lock) {
+                       return closed;
+               }
+       }
+
+       abstract void close_device();
+
+       public void close() {
+               AltosDebug.debug("close(): begin");
+
+               closing();
+
+               flush_output();
+
+               synchronized (closed_lock) {
+                       AltosDebug.debug("Marked closed true");
+                       closed = true;
+               }
+
+               close_device();
+
+               synchronized(this) {
+
+                       if (input_thread != null) {
+                               AltosDebug.debug("close(): stopping input_thread");
+                               try {
+                                       AltosDebug.debug("close(): input_thread.interrupt().....");
+                                       input_thread.interrupt();
+                                       AltosDebug.debug("close(): input_thread.join().....");
+                                       input_thread.join();
+                               } catch (Exception e) {}
+                               input_thread = null;
+                       }
+                       notifyAll();
+               }
+       }
+
+       abstract int write(byte[] buffer, int len);
+
+       abstract int read(byte[] buffer, int len);
+
+       private static final int buffer_size = 64;
+
+       private byte[] in_buffer = new byte[buffer_size];
+       private byte[] out_buffer = new byte[buffer_size];
+       private int buffer_len = 0;
+       private int buffer_off = 0;
+       private int out_buffer_off = 0;
+
+       private byte[] debug_chars = new byte[buffer_size];
+       private int debug_off;
+
+       private void debug_input(byte b) {
+               if (b == '\n') {
+                       AltosDebug.debug("            " + new String(debug_chars, 0, debug_off));
+                       debug_off = 0;
+               } else {
+                       if (debug_off < buffer_size)
+                               debug_chars[debug_off++] = b;
+               }
+       }
+
+       private void disconnected() {
+               if (closed()) {
+                       AltosDebug.debug("disconnected after closed");
+                       return;
+               }
+
+               AltosDebug.debug("Sending disconnected message");
+               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+       }
+
+       public int getchar() {
+
+               if (actually_closed())
+                       return ERROR;
+
+               while (buffer_off == buffer_len) {
+                       buffer_len = read(in_buffer, buffer_size);
+                       if (buffer_len < 0) {
+                               AltosDebug.debug("ERROR returned from getchar()");
+                               disconnected();
+                               return ERROR;
+                       }
+                       buffer_off = 0;
+               }
+               if (AltosDebug.D)
+                       debug_input(in_buffer[buffer_off]);
+               return in_buffer[buffer_off++];
+       }
+
+       public void flush_output() {
+               super.flush_output();
+
+               if (actually_closed()) {
+                       out_buffer_off = 0;
+                       return;
+               }
+
+               while (out_buffer_off != 0) {
+                       int     sent = write(out_buffer, out_buffer_off);
+
+                       if (sent <= 0) {
+                               AltosDebug.debug("flush_output() failed");
+                               out_buffer_off = 0;
+                               break;
+                       }
+
+                       if (sent < out_buffer_off)
+                               System.arraycopy(out_buffer, 0, out_buffer, sent, out_buffer_off - sent);
+
+                       out_buffer_off -= sent;
+               }
+       }
+
+       public void putchar(byte c) {
+               out_buffer[out_buffer_off++] = c;
+               if (out_buffer_off == buffer_size)
+                       flush_output();
+       }
+
+       public void print(String data) {
+               byte[] bytes = data.getBytes();
+               AltosDebug.debug("print(): begin");
+               for (byte b : bytes)
+                       putchar(b);
+               AltosDebug.debug("print(): Wrote bytes: '" + data.replace('\n', '\\') + "'");
+       }
+
+       public AltosDroidLink(Handler handler) {
+               this.handler = handler;
+       }
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosDroidMapInterface.java
new file mode 100644 (file)
index 0000000..5f6ff19
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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.util.*;
+import java.io.*;
+import android.location.Location;
+import org.altusmetrum.altoslib_8.*;
+
+public interface AltosDroidMapInterface {
+       public void onCreateView(AltosDroid altos_droid);
+
+       public void set_visible(boolean visible);
+
+       public void center(double lat, double lon, double accuracy);
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
+}
index 7ab701472bca37208d7bdb177b41266e46346548..a4e27006ee37ab0d2d19e745877f305760ec524b 100644 (file)
 package org.altusmetrum.AltosDroid;
 
 import android.content.Context;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosDroidPreferences extends AltosPreferences {
 
        /* Active device preference name */
-       final static String activeDevicePreference = "ACTIVE-DEVICE";
+       final static String activeDeviceAddressPreference = "ACTIVE-DEVICE-ADDRESS";
+       final static String activeDeviceNamePreference = "ACTIVE-DEVICE-NAME";
 
-       static String active_device_address;
+       static DeviceAddress    active_device_address;
+
+       /* Map source preference name */
+       final static String mapSourcePreference = "MAP-SOURCE";
+
+       static final int        MAP_SOURCE_OFFLINE = 0;
+       static final int        MAP_SOURCE_ONLINE = 1;
+
+       static int      map_source;
 
        public static void init(Context context) {
                if (backend != null)
@@ -32,20 +41,41 @@ public class AltosDroidPreferences extends AltosPreferences {
 
                AltosPreferences.init(new AltosDroidPreferencesBackend(context));
 
-               active_device_address = backend.getString(activeDevicePreference, null);
+               String address = backend.getString(activeDeviceAddressPreference, null);
+               String name = backend.getString(activeDeviceNamePreference, null);
+
+               if (address != null && name != null)
+                       active_device_address = new DeviceAddress (address, name);
+
+               map_source = backend.getInt(mapSourcePreference, MAP_SOURCE_ONLINE);
        }
 
-       public static void set_active_device(String address) {
+       public static void set_active_device(DeviceAddress address) {
                synchronized(backend) {
                        active_device_address = address;
-                       backend.putString(activeDevicePreference, active_device_address);
+                       backend.putString(activeDeviceAddressPreference, active_device_address.address);
+                       backend.putString(activeDeviceNamePreference, active_device_address.name);
                        flush_preferences();
                }
        }
 
-       public static String active_device() {
+       public static DeviceAddress active_device() {
                synchronized(backend) {
                        return active_device_address;
                }
        }
+
+       public static void set_map_source(int map_source) {
+               synchronized(backend) {
+                       AltosDroidPreferences.map_source = map_source;
+                       backend.putInt(mapSourcePreference, map_source);
+                       flush_preferences();
+               }
+       }
+
+       public static int map_source() {
+               synchronized(backend) {
+                       return map_source;
+               }
+       }
 }
index bc5300fdc36a3fd3cf0466f5939a368813500eb9..2ff711f57fee683abfe82a011b9bdc5c5307c2e5 100644 (file)
@@ -24,7 +24,7 @@ import android.content.SharedPreferences;
 import android.os.Environment;
 import android.util.*;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
        public final static String        NAME    = "org.altusmetrum.AltosDroid";
@@ -44,7 +44,12 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
 
        public String[] keys() {
                Map<String, ?> all = prefs.getAll();
-               return (String[])all.keySet().toArray();
+               Object[] ao = all.keySet().toArray();
+
+               String[] as = new String[ao.length];
+               for (int i = 0; i < ao.length; i++)
+                       as[i] = (String) ao[i];
+               return as;
        }
 
        public AltosPreferencesBackend node(String key) {
@@ -104,6 +109,7 @@ public class AltosDroidPreferencesBackend implements AltosPreferencesBackend {
        }
 
        public void remove(String key) {
+               AltosDebug.debug("remove preference %s\n", key);
                editor.remove(key);
        }
 
index cbb200450dc0b81270ca1d7e8b8092e746d29180..9d612a1ed897ba33ae3036694a9318c867080737 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 import android.location.Location;
 import android.app.Activity;
 import android.graphics.Color;
@@ -26,21 +26,28 @@ import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentTransaction;
 import android.support.v4.app.FragmentManager;
 import android.location.Location;
-import android.util.Log;
 import android.widget.TextView;
 
 public abstract class AltosDroidTab extends Fragment implements AltosUnitsListener {
+       TelemetryState          last_telem_state;
        AltosState              last_state;
        AltosGreatCircle        last_from_receiver;
        Location                last_receiver;
+       AltosDroid              altos_droid;
 
-       public abstract void show(AltosState state, AltosGreatCircle from_receiver, Location receiver);
+       public abstract void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver);
 
        public abstract String tab_name();
 
+       public void set_map_type(int map_type) {
+       }
+
+       public void set_map_source(int map_source) {
+       }
+
        public void units_changed(boolean imperial_units) {
-               if (!isHidden() && last_state != null)
-                       show(last_state, last_from_receiver, last_receiver);
+               if (!isHidden())
+                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
        }
 
        public void set_value(TextView text_view,
@@ -55,29 +62,46 @@ public abstract class AltosDroidTab extends Fragment implements AltosUnitsListen
 
        public void set_visible(boolean visible) {
                FragmentTransaction     ft = AltosDroid.fm.beginTransaction();
+               AltosDebug.debug("set visible %b %s\n", visible, tab_name());
                if (visible) {
-                       AltosState              state = last_state;
-                       AltosGreatCircle        from_receiver = last_from_receiver;
-                       Location                receiver = last_receiver;
-
-                       show(state, from_receiver, receiver);
                        ft.show(this);
+                       show(last_telem_state, last_state, last_from_receiver, last_receiver);
                } else
                        ft.hide(this);
-               ft.commit();
+               ft.commitAllowingStateLoss();
+       }
+
+       @Override
+       public void onAttach(Activity activity) {
+               super.onAttach(activity);
+               altos_droid = (AltosDroid) activity;
+               altos_droid.registerTab(this);
        }
 
-       public void update_ui(AltosState state, AltosGreatCircle from_receiver, Location receiver, boolean is_current) {
+       @Override
+       public void onDetach() {
+               super.onDetach();
+               altos_droid.unregisterTab(this);
+               altos_droid = null;
+       }
+
+       @Override
+       public void onResume() {
+               super.onResume();
+               AltosDebug.debug("onResume tab %s\n", tab_name());
+               set_visible(true);
+       }
+
+       public void update_ui(TelemetryState telem_state, AltosState state,
+                             AltosGreatCircle from_receiver, Location receiver, boolean is_current)
+       {
+               last_telem_state = telem_state;
                last_state = state;
                last_from_receiver = from_receiver;
                last_receiver = receiver;
-               if (is_current) {
-                       if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: visible, performing update", tab_name()));
-
-                       show(state, from_receiver, receiver);
-               } else {
-                       if (AltosDroid.D) Log.d(AltosDroid.TAG, String.format("%s: not visible, skipping update", tab_name()));
+               if (is_current)
+                       show(telem_state, state, from_receiver, receiver);
+               else
                        return;
-               }
        }
 }
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOffline.java
new file mode 100644 (file)
index 0000000..eb05990
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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.util.*;
+import java.io.*;
+
+import org.altusmetrum.altoslib_8.*;
+
+import android.app.Activity;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+import android.content.*;
+import android.util.*;
+
+class Rocket implements Comparable {
+       AltosLatLon     position;
+       String          name;
+       int             serial;
+       long            last_packet;
+       boolean         active;
+       AltosMapOffline map_offline;
+
+       void paint() {
+               map_offline.draw_bitmap(position, map_offline.rocket_bitmap, map_offline.rocket_off_x, map_offline.rocket_off_y);
+               map_offline.draw_text(position, name, 0, 3*map_offline.rocket_bitmap.getHeight()/4);
+       }
+
+       void set_position(AltosLatLon position, long last_packet) {
+               this.position = position;
+               this.last_packet = last_packet;
+       }
+
+       void set_active(boolean active) {
+               this.active = active;
+       }
+
+       public int compareTo(Object o) {
+               Rocket other = (Rocket) o;
+
+               if (active && !other.active)
+                       return 1;
+               if (other.active && !active)
+                       return -1;
+
+               long    diff = last_packet - other.last_packet;
+
+               if (diff > 0)
+                       return 1;
+               if (diff < 0)
+                       return -1;
+               return 0;
+       }
+
+       Rocket(int serial, AltosMapOffline map_offline) {
+               this.serial = serial;
+               this.name = String.format("%d", serial);
+               this.map_offline = map_offline;
+       }
+}
+
+public class AltosMapOffline extends View implements ScaleGestureDetector.OnScaleGestureListener, AltosMapInterface, AltosDroidMapInterface {
+       ScaleGestureDetector    scale_detector;
+       boolean                 scaling;
+       AltosMap                map;
+       AltosDroid              altos_droid;
+
+       AltosLatLon     here;
+       AltosLatLon     there;
+       AltosLatLon     pad;
+
+       Canvas  canvas;
+       Paint   paint;
+
+       Bitmap  pad_bitmap;
+       int     pad_off_x, pad_off_y;
+       Bitmap  rocket_bitmap;
+       int     rocket_off_x, rocket_off_y;
+       Bitmap  here_bitmap;
+       int     here_off_x, here_off_y;
+
+       static  final int       WHITE = 0xffffffff;
+       static  final int       RED   = 0xffff0000;
+       static  final int       PINK  = 0xffff8080;
+       static  final int       YELLOW= 0xffffff00;
+       static  final int       CYAN  = 0xff00ffff;
+       static  final int       BLUE  = 0xff0000ff;
+       static  final int       BLACK = 0xff000000;
+
+       public static final int stateColors[] = {
+               WHITE,  // startup
+               WHITE,  // idle
+               WHITE,  // pad
+               RED,    // boost
+               PINK,   // fast
+               YELLOW, // coast
+               CYAN,   // drogue
+               BLUE,   // main
+               BLACK,  // landed
+               BLACK,  // invalid
+               CYAN,   // stateless
+       };
+
+       /* AltosMapInterface */
+       public void debug(String format, Object ... arguments) {
+               AltosDebug.debug(format, arguments);
+       }
+
+       class MapTile extends AltosMapTile {
+               public void paint(AltosMapTransform t) {
+                       AltosPointInt           pt = new AltosPointInt(t.screen(upper_left));
+
+                       if (canvas.quickReject(pt.x, pt.y, pt.x + px_size, pt.y + px_size, Canvas.EdgeType.AA))
+                               return;
+
+                       AltosImage              altos_image = cache.get(this, store, px_size, px_size);
+
+                       MapImage                map_image = (MapImage) altos_image;
+
+                       Bitmap                  bitmap = null;
+
+                       if (map_image != null)
+                               bitmap = map_image.bitmap;
+
+                       if (bitmap != null) {
+                               canvas.drawBitmap(bitmap, pt.x, pt.y, paint);
+                       } else {
+                               paint.setColor(0xff808080);
+                               canvas.drawRect(pt.x, pt.y, pt.x + px_size, pt.y + px_size, paint);
+                               if (t.has_location()) {
+                                       String  message = null;
+                                       switch (status) {
+                                       case AltosMapTile.loading:
+                                               message = "Loading...";
+                                               break;
+                                       case AltosMapTile.bad_request:
+                                               message = "Internal error";
+                                               break;
+                                       case AltosMapTile.failed:
+                                               message = "Network error, check connection";
+                                               break;
+                                       case AltosMapTile.forbidden:
+                                               message = "Too many requests, try later";
+                                               break;
+                                       }
+                                       if (message != null) {
+                                               Rect    bounds = new Rect();
+                                               paint.getTextBounds(message, 0, message.length(), bounds);
+
+                                               int     width = bounds.right - bounds.left;
+                                               int     height = bounds.bottom - bounds.top;
+
+                                               float x = pt.x + px_size / 2.0f;
+                                               float y = pt.y + px_size / 2.0f;
+                                               x = x - width / 2.0f;
+                                               y = y + height / 2.0f;
+                                               paint.setColor(0xff000000);
+                                               canvas.drawText(message, 0, message.length(), x, y, paint);
+                                       }
+                               }
+                       }
+               }
+
+               public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+                       super(listener, upper_left, center, zoom, maptype, px_size, 2);
+               }
+
+       }
+
+       public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+               return new MapTile(listener, upper_left, center, zoom, maptype, px_size);
+       }
+
+       public AltosMapPath new_path() {
+               return null;
+       }
+
+       public AltosMapLine new_line() {
+               return null;
+       }
+
+       class MapImage implements AltosImage {
+               public Bitmap   bitmap;
+
+               public void flush() {
+                       if (bitmap != null) {
+                               bitmap.recycle();
+                               bitmap = null;
+                       }
+               }
+
+               public MapImage(File file) {
+                       bitmap = BitmapFactory.decodeFile(file.getPath());
+               }
+       }
+
+       public AltosImage load_image(File file) throws Exception {
+               return new MapImage(file);
+       }
+
+       class MapMark extends AltosMapMark {
+               public void paint(AltosMapTransform t) {
+               }
+
+               MapMark(double lat, double lon, int state) {
+                       super(lat, lon, state);
+               }
+       }
+
+       public AltosMapMark new_mark(double lat, double lon, int state) {
+               return new MapMark(lat, lon, state);
+       }
+
+       public int width() {
+               return getWidth();
+       }
+
+       public int height() {
+               return getHeight();
+       }
+
+       public void repaint() {
+               postInvalidate();
+       }
+
+       public void repaint(AltosRectangle damage) {
+               postInvalidate(damage.x, damage.y, damage.x + damage.width, damage.y + damage.height);
+       }
+
+       public void set_zoom_label(String label) {
+       }
+
+       public void select_object(AltosLatLon latlon) {
+               if (map.transform == null)
+                       return;
+               ArrayList<Integer>      near = new ArrayList<Integer>();
+
+               for (Rocket rocket : sorted_rockets()) {
+                       if (rocket.position == null) {
+                               debug("rocket %d has no position\n", rocket.serial);
+                               continue;
+                       }
+                       double distance = map.transform.hypot(latlon, rocket.position);
+                       debug("check select %d distance %g width %d\n", rocket.serial, distance, rocket_bitmap.getWidth());
+                       if (distance < rocket_bitmap.getWidth() * 2.0) {
+                               debug("selecting %d\n", rocket.serial);
+                               near.add(rocket.serial);
+                       }
+               }
+               if (near.size() != 0)
+                       altos_droid.touch_trackers(near.toArray(new Integer[0]));
+       }
+
+       class Line {
+               AltosLatLon     a, b;
+
+               void paint() {
+                       if (a != null && b != null) {
+                               AltosPointDouble        a_screen = map.transform.screen(a);
+                               AltosPointDouble        b_screen = map.transform.screen(b);
+                               paint.setColor(0xff8080ff);
+                               canvas.drawLine((float) a_screen.x, (float) a_screen.y,
+                                                   (float) b_screen.x, (float) b_screen.y,
+                                                   paint);
+                       }
+               }
+
+               void set_a(AltosLatLon a) {
+                       this.a = a;
+               }
+
+               void set_b(AltosLatLon b) {
+                       this.b = b;
+               }
+
+               Line() {
+               }
+       }
+
+       Line line = new Line();
+
+       int     stroke_width = 20;
+
+       void draw_text(AltosLatLon lat_lon, String text, int off_x, int off_y) {
+               if (lat_lon != null && map != null && map.transform != null) {
+                       AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
+
+                       Rect    bounds = new Rect();
+                       paint.getTextBounds(text, 0, text.length(), bounds);
+
+                       int     width = bounds.right - bounds.left;
+                       int     height = bounds.bottom - bounds.top;
+
+                       float x = pt.x;
+                       float y = pt.y;
+                       x = x - width / 2.0f - off_x;
+                       y = y + height / 2.0f - off_y;
+                       paint.setColor(0xff000000);
+                       canvas.drawText(text, 0, text.length(), x, y, paint);
+               }
+       }
+
+       HashMap<Integer,Rocket> rockets = new HashMap<Integer,Rocket>();
+
+       void draw_bitmap(AltosLatLon lat_lon, Bitmap bitmap, int off_x, int off_y) {
+               if (lat_lon != null && map != null && map.transform != null) {
+                       AltosPointInt pt = new AltosPointInt(map.transform.screen(lat_lon));
+
+                       canvas.drawBitmap(bitmap, pt.x - off_x, pt.y - off_y, paint);
+               }
+       }
+
+       private Rocket[] sorted_rockets() {
+               Rocket[]        rocket_array = rockets.values().toArray(new Rocket[0]);
+
+               Arrays.sort(rocket_array);
+               return rocket_array;
+       }
+
+       private void draw_positions() {
+               line.set_a(there);
+               line.set_b(here);
+               line.paint();
+               draw_bitmap(pad, pad_bitmap, pad_off_x, pad_off_y);
+
+               for (Rocket rocket : sorted_rockets())
+                       rocket.paint();
+               draw_bitmap(here, here_bitmap, here_off_x, here_off_y);
+       }
+
+       @Override public void invalidate() {
+               Rect r = new Rect();
+               getDrawingRect(r);
+               super.invalidate();
+       }
+
+       @Override public void invalidate(int l, int t, int r, int b) {
+               Rect rect = new Rect();
+               getDrawingRect(rect);
+               super.invalidate();
+       }
+
+       @Override
+       protected void onDraw(Canvas view_canvas) {
+               if (map == null) {
+                       debug("MapView draw without map\n");
+                       return;
+               }
+               canvas = view_canvas;
+               paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+               paint.setStrokeWidth(stroke_width);
+               paint.setStrokeCap(Paint.Cap.ROUND);
+               paint.setStrokeJoin(Paint.Join.ROUND);
+               paint.setTextSize(40);
+               map.paint();
+               draw_positions();
+               canvas = null;
+       }
+
+       public boolean onScale(ScaleGestureDetector detector) {
+               float   f = detector.getScaleFactor();
+
+               if (f <= 0.8) {
+                       map.set_zoom_centre(map.get_zoom() - 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
+                       return true;
+               }
+               if (f >= 1.2) {
+                       map.set_zoom_centre(map.get_zoom() + 1, new AltosPointInt((int) detector.getFocusX(), (int) detector.getFocusY()));
+                       return true;
+               }
+               return false;
+       }
+
+       public boolean onScaleBegin(ScaleGestureDetector detector) {
+               return true;
+       }
+
+       public void onScaleEnd(ScaleGestureDetector detector) {
+       }
+
+       @Override
+       public boolean dispatchTouchEvent(MotionEvent event) {
+               scale_detector.onTouchEvent(event);
+
+               if (scale_detector.isInProgress()) {
+                       scaling = true;
+               }
+
+               if (scaling) {
+                       if (event.getAction() == MotionEvent.ACTION_UP) {
+                               scaling = false;
+                       }
+                       return true;
+               }
+
+               if (event.getAction() == MotionEvent.ACTION_DOWN) {
+                       map.touch_start((int) event.getX(), (int) event.getY(), true);
+               } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
+                       map.touch_continue((int) event.getX(), (int) event.getY(), true);
+               } else if (event.getAction() == MotionEvent.ACTION_UP) {
+                       map.touch_stop((int) event.getX(), (int) event.getY(), true);
+               }
+               return true;
+       }
+
+       double  mapAccuracy;
+
+       public void center(double lat, double lon, double accuracy) {
+               if (mapAccuracy <= 0 || accuracy < mapAccuracy/10 || (map != null && !map.has_centre())) {
+                       if (map != null)
+                               map.maybe_centre(lat, lon);
+                       mapAccuracy = accuracy;
+               }
+       }
+
+       public void set_visible(boolean visible) {
+               if (visible)
+                       setVisibility(VISIBLE);
+               else
+                       setVisibility(GONE);
+       }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (state != null) {
+                       map.show(state, null);
+                       if (state.pad_lat != AltosLib.MISSING && pad == null)
+                               pad = new AltosLatLon(state.pad_lat, state.pad_lon);
+               }
+
+               if (telem_state != null) {
+                       Integer[] old_serial = rockets.keySet().toArray(new Integer[0]);
+                       Integer[] new_serial = telem_state.states.keySet().toArray(new Integer[0]);
+
+                       /* remove deleted keys */
+                       for (int serial : old_serial) {
+                               if (!telem_state.states.containsKey(serial))
+                                       rockets.remove(serial);
+                       }
+
+                       /* set remaining keys */
+
+                       for (int serial : new_serial) {
+                               Rocket          rocket;
+                               AltosState      t_state = telem_state.states.get(serial);
+                               if (rockets.containsKey(serial))
+                                       rocket = rockets.get(serial);
+                               else {
+                                       rocket = new Rocket(serial, this);
+                                       rockets.put(serial, rocket);
+                               }
+                               if (t_state.gps != null) {
+                                       AltosLatLon     latlon = new AltosLatLon(t_state.gps.lat, t_state.gps.lon);
+                                       rocket.set_position(latlon, t_state.received_time);
+                                       if (state.serial == serial)
+                                               there = latlon;
+                               }
+                               if (state != null)
+                                       rocket.set_active(state.serial == serial);
+                       }
+               }
+               if (receiver != null) {
+                       here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
+               }
+       }
+
+       public void onCreateView(AltosDroid altos_droid) {
+               this.altos_droid = altos_droid;
+               map = new AltosMap(this);
+               map.set_maptype(altos_droid.map_type);
+
+               pad_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pad);
+               /* arrow at the bottom of the launchpad image */
+               pad_off_x = pad_bitmap.getWidth() / 2;
+               pad_off_y = pad_bitmap.getHeight();
+
+               rocket_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.rocket);
+               /* arrow at the bottom of the rocket image */
+               rocket_off_x = rocket_bitmap.getWidth() / 2;
+               rocket_off_y = rocket_bitmap.getHeight();
+
+               here_bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_maps_indicator_current_position);
+               /* Center of the dot */
+               here_off_x = here_bitmap.getWidth() / 2;
+               here_off_y = here_bitmap.getHeight() / 2;
+       }
+
+       public void set_map_type(int map_type) {
+               if (map != null)
+                       map.set_maptype(map_type);
+       }
+
+       public AltosMapOffline(Context context, AttributeSet attrs) {
+               super(context, attrs);
+               this.altos_droid = altos_droid;
+               scale_detector = new ScaleGestureDetector(context, this);
+       }
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosMapOnline.java
new file mode 100644 (file)
index 0000000..4ac95c0
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright © 2013 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.util.*;
+
+import org.altusmetrum.altoslib_8.*;
+
+import com.google.android.gms.maps.*;
+import com.google.android.gms.maps.model.*;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.graphics.*;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+//import android.support.v4.app.FragmentTransaction;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.location.Location;
+import android.content.*;
+
+class RocketOnline implements Comparable {
+       Marker          marker;
+       int             serial;
+       long            last_packet;
+       int             size;
+
+       void set_position(AltosLatLon position, long last_packet) {
+               marker.setPosition(new LatLng(position.lat, position.lon));
+               this.last_packet = last_packet;
+       }
+
+       private Bitmap rocket_bitmap(Context context, String text) {
+
+               /* From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
+                */
+               Bitmap orig_bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.rocket);
+               Bitmap bitmap = orig_bitmap.copy(Bitmap.Config.ARGB_8888, true);
+
+               Canvas canvas = new Canvas(bitmap);
+               Paint paint = new Paint();
+               paint.setTextSize(40);
+               paint.setColor(0xff000000);
+
+               Rect    bounds = new Rect();
+               paint.getTextBounds(text, 0, text.length(), bounds);
+
+               int     width = bounds.right - bounds.left;
+               int     height = bounds.bottom - bounds.top;
+
+               float x = bitmap.getWidth() / 2.0f - width / 2.0f;
+               float y = bitmap.getHeight() / 2.0f - height / 2.0f;
+
+               size = bitmap.getWidth();
+
+               canvas.drawText(text, 0, text.length(), x, y, paint);
+               return bitmap;
+       }
+
+       public void remove() {
+               marker.remove();
+       }
+
+       public int compareTo(Object o) {
+               RocketOnline other = (RocketOnline) o;
+
+               long    diff = last_packet - other.last_packet;
+
+               if (diff > 0)
+                       return 1;
+               if (diff < 0)
+                       return -1;
+               return 0;
+       }
+
+       RocketOnline(Context context, int serial, GoogleMap map, double lat, double lon, long last_packet) {
+               this.serial = serial;
+               String name = String.format("%d", serial);
+               this.marker = map.addMarker(new MarkerOptions()
+                                           .icon(BitmapDescriptorFactory.fromBitmap(rocket_bitmap(context, name)))
+                                           .position(new LatLng(lat, lon))
+                                           .visible(true));
+               this.last_packet = last_packet;
+       }
+}
+
+public class AltosMapOnline implements AltosDroidMapInterface, GoogleMap.OnMarkerClickListener, GoogleMap.OnMapClickListener {
+       public SupportMapFragment mMapFragment;
+       private GoogleMap mMap;
+       private boolean mapLoaded = false;
+       Context context;
+
+       private HashMap<Integer,RocketOnline> rockets = new HashMap<Integer,RocketOnline>();
+       private Marker mPadMarker;
+       private boolean pad_set;
+       private Polyline mPolyline;
+
+       private View map_view;
+
+       private double mapAccuracy = -1;
+
+       private AltosLatLon my_position = null;
+       private AltosLatLon target_position = null;
+
+       private AltosDroid altos_droid;
+
+       public void onCreateView(AltosDroid altos_droid) {
+               this.altos_droid = altos_droid;
+               final int map_type = altos_droid.map_type;
+               mMapFragment = new SupportMapFragment() {
+                       @Override
+                       public void onActivityCreated(Bundle savedInstanceState) {
+                               super.onActivityCreated(savedInstanceState);
+                               setupMap(map_type);
+                       }
+                       @Override
+                       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+                               map_view = super.onCreateView(inflater, container, savedInstanceState);
+                               return map_view;
+                       }
+                       @Override
+                       public void onDestroyView() {
+                               super.onDestroyView();
+                               map_view = null;
+                       }
+               };
+       }
+
+//     public void onActivityCreated() {
+//             getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit();
+//     }
+
+       private double pixel_distance(LatLng a, LatLng b) {
+               Projection projection = mMap.getProjection();
+
+               Point   a_pt = projection.toScreenLocation(a);
+               Point   b_pt = projection.toScreenLocation(b);
+
+               return Math.hypot((double) (a_pt.x - b_pt.x), (double) (a_pt.y - b_pt.y));
+       }
+
+       private RocketOnline[] sorted_rockets() {
+               RocketOnline[]  rocket_array = rockets.values().toArray(new RocketOnline[0]);
+
+               Arrays.sort(rocket_array);
+               return rocket_array;
+       }
+
+       public void onMapClick(LatLng lat_lng) {
+               ArrayList<Integer>      near = new ArrayList<Integer>();
+
+               for (RocketOnline rocket : sorted_rockets()) {
+                       LatLng  pos = rocket.marker.getPosition();
+
+                       if (pos == null)
+                               continue;
+
+                       double distance = pixel_distance(lat_lng, pos);
+                       if (distance < rocket.size * 2)
+                               near.add(rocket.serial);
+               }
+
+               if (near.size() != 0)
+                       altos_droid.touch_trackers(near.toArray(new Integer[0]));
+       }
+
+       public boolean onMarkerClick(Marker marker) {
+               onMapClick(marker.getPosition());
+               return true;
+       }
+
+       public void setupMap(int map_type) {
+               mMap = mMapFragment.getMap();
+               if (mMap != null) {
+                       set_map_type(map_type);
+                       mMap.setMyLocationEnabled(true);
+                       mMap.getUiSettings().setTiltGesturesEnabled(false);
+                       mMap.getUiSettings().setZoomControlsEnabled(false);
+                       mMap.setOnMarkerClickListener(this);
+                       mMap.setOnMapClickListener(this);
+
+                       mPadMarker = mMap.addMarker(
+                                       new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
+                                                          .position(new LatLng(0,0))
+                                                          .visible(false)
+                                       );
+
+                       mPolyline = mMap.addPolyline(
+                                       new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
+                                                            .width(20)
+                                                            .color(Color.BLUE)
+                                                            .visible(false)
+                                       );
+
+                       mapLoaded = true;
+               }
+       }
+
+       public void center(double lat, double lon, double accuracy) {
+               if (mMap == null)
+                       return;
+
+               if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
+                       mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
+                       mapAccuracy = accuracy;
+               }
+       }
+
+       private void set_rocket(int serial, AltosState state) {
+               RocketOnline    rocket;
+
+               if (state.gps == null || state.gps.lat == AltosLib.MISSING)
+                       return;
+
+               if (mMap == null)
+                       return;
+
+               if (rockets.containsKey(serial)) {
+                       rocket = rockets.get(serial);
+                       rocket.set_position(new AltosLatLon(state.gps.lat, state.gps.lon), state.received_time);
+               } else {
+                       rocket = new RocketOnline(context,
+                                                 serial,
+                                                 mMap, state.gps.lat, state.gps.lon,
+                                                 state.received_time);
+                       rockets.put(serial, rocket);
+               }
+       }
+
+       private void remove_rocket(int serial) {
+               RocketOnline rocket = rockets.get(serial);
+               rocket.remove();
+               rockets.remove(serial);
+       }
+
+       public void set_visible(boolean visible) {
+               if (map_view == null)
+                       return;
+               if (visible)
+                       map_view.setVisibility(View.VISIBLE);
+               else
+                       map_view.setVisibility(View.GONE);
+       }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+
+               if (telem_state != null) {
+                       for (int serial : rockets.keySet()) {
+                               if (!telem_state.states.containsKey(serial))
+                                       remove_rocket(serial);
+                       }
+
+                       for (int serial : telem_state.states.keySet()) {
+                               set_rocket(serial, telem_state.states.get(serial));
+                       }
+               }
+
+               if (state != null) {
+                       if (mapLoaded) {
+                               if (!pad_set && state.pad_lat != AltosLib.MISSING) {
+                                       pad_set = true;
+                                       mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
+                                       mPadMarker.setVisible(true);
+                               }
+                       }
+                       if (state.gps != null) {
+
+                               target_position = new AltosLatLon(state.gps.lat, state.gps.lon);
+                               if (state.gps.locked && state.gps.nsat >= 4)
+                                       center (state.gps.lat, state.gps.lon, 10);
+                       }
+               }
+
+               if (receiver != null) {
+                       double accuracy;
+
+                       if (receiver.hasAccuracy())
+                               accuracy = receiver.getAccuracy();
+                       else
+                               accuracy = 1000;
+
+                       my_position = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
+                       center (my_position.lat, my_position.lon, accuracy);
+               }
+
+               if (my_position != null && target_position != null && mPolyline != null) {
+                       mPolyline.setPoints(Arrays.asList(new LatLng(my_position.lat, my_position.lon), new LatLng(target_position.lat, target_position.lon)));
+                       mPolyline.setVisible(true);
+               }
+
+       }
+
+       public void set_map_type(int map_type) {
+               if (mMap != null) {
+                       if (map_type == AltosMap.maptype_hybrid)
+                               mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
+                       else if (map_type == AltosMap.maptype_satellite)
+                               mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
+                       else if (map_type == AltosMap.maptype_terrain)
+                               mMap.setMapType(GoogleMap.MAP_TYPE_TERRAIN);
+                       else
+                               mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
+               }
+       }
+
+       public AltosMapOnline(Context context) {
+               this.context = context;
+       }
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java b/altosdroid/src/org/altusmetrum/AltosDroid/AltosUsb.java
new file mode 100644 (file)
index 0000000..b7eb76a
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 java.util.HashMap;
+
+import android.content.Context;
+import android.hardware.usb.*;
+import android.app.*;
+import android.os.Handler;
+
+import org.altusmetrum.altoslib_8.*;
+
+public class AltosUsb extends AltosDroidLink {
+
+       private Thread           input_thread   = null;
+
+       private Handler          handler;
+
+       private UsbManager              manager;
+       private UsbDevice               device;
+       private UsbDeviceConnection     connection;
+       private UsbInterface            iface;
+       private UsbEndpoint             in, out;
+
+       private InputStream      input;
+       private OutputStream     output;
+
+       // Constructor
+       public AltosUsb(Context context, UsbDevice device, Handler handler) {
+               super(handler);
+//             set_debug(D);
+               this.handler = handler;
+
+               iface = null;
+               in = null;
+               out = null;
+
+               int     niface = device.getInterfaceCount();
+
+               for (int i = 0; i < niface; i++) {
+
+                       iface = device.getInterface(i);
+
+                       in = null;
+                       out = null;
+
+                       int nendpoints = iface.getEndpointCount();
+
+                       for (int e = 0; e < nendpoints; e++) {
+                               UsbEndpoint     endpoint = iface.getEndpoint(e);
+
+                               if (endpoint.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
+                                       switch (endpoint.getDirection()) {
+                                       case UsbConstants.USB_DIR_OUT:
+                                               out = endpoint;
+                                               break;
+                                       case UsbConstants.USB_DIR_IN:
+                                               in = endpoint;
+                                               break;
+                                       }
+                               }
+                       }
+
+                       if (in != null && out != null)
+                               break;
+               }
+
+               if (in != null && out != null) {
+                       AltosDebug.debug("\tin %s out %s\n", in.toString(), out.toString());
+
+                       manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+                       if (manager == null) {
+                               AltosDebug.debug("USB_SERVICE failed");
+                               return;
+                       }
+
+                       connection = manager.openDevice(device);
+
+                       if (connection == null) {
+                               AltosDebug.debug("openDevice failed");
+                               return;
+                       }
+
+                       connection.claimInterface(iface, true);
+
+                       input_thread = new Thread(this);
+                       input_thread.start();
+
+                       // Configure the newly connected device for telemetry
+                       print("~\nE 0\n");
+                       set_monitor(false);
+               }
+       }
+
+       static private boolean isAltusMetrum(UsbDevice device) {
+               if (device.getVendorId() != AltosLib.vendor_altusmetrum)
+                       return false;
+               if (device.getProductId() < AltosLib.product_altusmetrum_min)
+                       return false;
+               if (device.getProductId() > AltosLib.product_altusmetrum_max)
+                       return false;
+               return true;
+       }
+
+       static boolean matchProduct(int want_product, UsbDevice device) {
+
+               if (!isAltusMetrum(device))
+                       return false;
+
+               if (want_product == AltosLib.product_any)
+                       return true;
+
+               int have_product = device.getProductId();
+
+               if (want_product == AltosLib.product_basestation)
+                       return have_product == AltosLib.product_teledongle ||
+                               have_product == AltosLib.product_teleterra ||
+                               have_product == AltosLib.product_telebt ||
+                               have_product == AltosLib.product_megadongle;
+
+               if (want_product == AltosLib.product_altimeter)
+                       return have_product == AltosLib.product_telemetrum ||
+                               have_product == AltosLib.product_telemega ||
+                               have_product == AltosLib.product_easymega ||
+                               have_product == AltosLib.product_telegps ||
+                               have_product == AltosLib.product_easymini ||
+                               have_product == AltosLib.product_telemini;
+
+               if (have_product == AltosLib.product_altusmetrum)       /* old devices match any request */
+                       return true;
+
+               if (want_product == have_product)
+                       return true;
+
+               return false;
+       }
+
+       static public boolean request_permission(Context context, UsbDevice device, PendingIntent pi) {
+               UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+//             if (manager.hasPermission(device))
+//                     return true;
+
+               AltosDebug.debug("request permission for USB device " + device.toString());
+
+               manager.requestPermission(device, pi);
+               return false;
+       }
+
+       static public UsbDevice find_device(Context context, int match_product) {
+               UsbManager      manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
+
+               HashMap<String,UsbDevice>       devices = manager.getDeviceList();
+
+               for (UsbDevice  device : devices.values()) {
+                       int     vendor = device.getVendorId();
+                       int     product = device.getProductId();
+
+                       if (matchProduct(match_product, device)) {
+                               AltosDebug.debug("found USB device " + device.toString());
+                               return device;
+                       }
+               }
+
+               return null;
+       }
+
+       private void disconnected() {
+               if (closed()) {
+                       AltosDebug.debug("disconnected after closed");
+                       return;
+               }
+
+               AltosDebug.debug("Sending disconnected message");
+               handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, this).sendToTarget();
+       }
+
+       void close_device() {
+               UsbDeviceConnection     tmp_connection;
+
+               synchronized(this) {
+                       tmp_connection = connection;
+                       connection = null;
+               }
+
+               if (tmp_connection != null) {
+                       AltosDebug.debug("Closing USB device");
+                       tmp_connection.close();
+               }
+       }
+
+       int read(byte[] buffer, int len) {
+               int ret = connection.bulkTransfer(in, buffer, len, -1);
+               AltosDebug.debug("read(%d) = %d\n", len, ret);
+               return ret;
+       }
+
+       int write(byte[] buffer, int len) {
+               int ret = connection.bulkTransfer(out, buffer, len, -1);
+               AltosDebug.debug("write(%d) = %d\n", len, ret);
+               return ret;
+       }
+
+       // 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()    { }
+
+}
index 223ae75a0e48f9e99f91f0b85a758ee260e08730..f22298d73d0a49a82c0c8a66c474cd82180a9041 100644 (file)
@@ -34,14 +34,19 @@ public class AltosViewPager extends ViewPager {
 
     @Override
     protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
-       if(v.getClass() != null &&
-          v.getClass().getPackage() != null &&
-          v.getClass().getPackage().getName() != null &&
-          v.getClass().getPackage().getName().startsWith("maps."))
-       {
-            return true;
-        }
-        return super.canScroll(v, checkV, dx, x, y);
+
+           if (v.getClass() != null &&
+               v.getClass().getName() != null &&
+               v.getClass().getName().endsWith("MapOffline"))
+                   return true;
+
+           if(v.getClass() != null &&
+              v.getClass().getPackage() != null &&
+              v.getClass().getPackage().getName() != null &&
+              v.getClass().getPackage().getName().startsWith("maps."))
+                   return true;
+
+           return super.canScroll(v, checkV, dx, x, y);
     }
 
 }
index 2d32dc071ffdc5240443cf72a0433279c7036ea2..325b89d2f553a0186a053b84cfc3c6108339fd12 100644 (file)
@@ -20,201 +20,305 @@ package org.altusmetrum.AltosDroid;
 
 import android.speech.tts.TextToSpeech;
 import android.speech.tts.TextToSpeech.OnInitListener;
+import android.location.Location;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosVoice {
 
        private TextToSpeech tts         = null;
        private boolean      tts_enabled = false;
 
-       private IdleThread   idle_thread = null;
+       static final int TELL_MODE_NONE = 0;
+       static final int TELL_MODE_PAD = 1;
+       static final int TELL_MODE_FLIGHT = 2;
+       static final int TELL_MODE_RECOVER = 3;
 
-       private AltosState   old_state   = null;
+       static final int TELL_FLIGHT_NONE = 0;
+       static final int TELL_FLIGHT_STATE = 1;
+       static final int TELL_FLIGHT_SPEED = 2;
+       static final int TELL_FLIGHT_HEIGHT = 3;
+       static final int TELL_FLIGHT_TRACK = 4;
 
-       public AltosVoice(AltosDroid a) {
+       private int             last_tell_mode;
+       private int             last_tell_serial = AltosLib.MISSING;
+       private int             last_state;
+       private AltosGPS        last_gps;
+       private double          last_height = AltosLib.MISSING;
+       private Location        last_receiver;
+       private long            last_speak_time;
+       private int             last_flight_tell = TELL_FLIGHT_NONE;
+
+       private long now() {
+               return System.currentTimeMillis();
+       }
+
+       private void reset_last() {
+               last_tell_mode = TELL_MODE_NONE;
+               last_speak_time = now() - 100 * 1000;
+               last_gps = null;
+               last_height = AltosLib.MISSING;
+               last_receiver = null;
+               last_state = AltosLib.ao_flight_invalid;
+               last_flight_tell = TELL_FLIGHT_NONE;
+       }
 
+       public AltosVoice(AltosDroid a) {
                tts = new TextToSpeech(a, new OnInitListener() {
                        public void onInit(int status) {
                                if (status == TextToSpeech.SUCCESS) tts_enabled = true;
-                               if (tts_enabled) {
-                                       idle_thread = new IdleThread();
-                               }
                        }
                });
+               reset_last();
+       }
 
+       public synchronized void set_enable(boolean enable) {
+               tts_enabled = enable;
        }
 
        public synchronized void speak(String s) {
                if (!tts_enabled) return;
+               last_speak_time = now();
                tts.speak(s, TextToSpeech.QUEUE_ADD, null);
        }
 
+       public synchronized long time_since_speak() {
+               return now() - last_speak_time;
+       }
+
+       public synchronized void speak(String format, Object ... arguments) {
+               speak(String.format(format, arguments));
+       }
+
+       public synchronized boolean is_speaking() {
+               return tts.isSpeaking();
+       }
+
        public void stop() {
-               if (tts != null) tts.shutdown();
-               if (idle_thread != null) {
-                       idle_thread.interrupt();
-                       idle_thread = null;
+               if (tts != null) {
+                       tts.stop();
+                       tts.shutdown();
                }
        }
 
-       public void tell(AltosState state, AltosGreatCircle from_receiver) {
-               if (!tts_enabled) return;
+       private boolean         last_apogee_good;
+       private boolean         last_main_good;
+       private boolean         last_gps_good;
 
-               boolean spoke = false;
-               if (old_state == null || old_state.state != state.state) {
-                       if (state.state != AltosLib.ao_flight_stateless)
-                               speak(state.state_name());
-                       if ((old_state == null || old_state.state <= AltosLib.ao_flight_boost) &&
-                           state.state > AltosLib.ao_flight_boost) {
-                               if (state.max_speed() != AltosLib.MISSING)
-                                       speak(String.format("Max speed: %s.",
-                                                           AltosConvert.speed.say_units(state.max_speed())));
-                               spoke = true;
-                       } else if ((old_state == null || old_state.state < AltosLib.ao_flight_drogue) &&
-                                  state.state >= AltosLib.ao_flight_drogue) {
-                               if (state.max_height() != AltosLib.MISSING)
-                                       speak(String.format("Max height: %s.",
-                                                           AltosConvert.height.say_units(state.max_height())));
-                               spoke = true;
-                       }
+       private boolean tell_gonogo(String name,
+                                 boolean current,
+                                 boolean previous,
+                                 boolean new_mode) {
+               if (current != previous || new_mode)
+                       speak("%s %s.", name, current ? "ready" : "not ready");
+               return current;
+       }
+
+       private boolean tell_pad(TelemetryState telem_state, AltosState state,
+                             AltosGreatCircle from_receiver, Location receiver) {
+
+               if (state == null)
+                       return false;
+
+               if (state.apogee_voltage != AltosLib.MISSING)
+                       last_apogee_good = tell_gonogo("apogee",
+                                                      state.apogee_voltage >= AltosLib.ao_igniter_good,
+                                                      last_apogee_good,
+                                                      last_tell_mode != TELL_MODE_PAD);
+
+               if (state.main_voltage != AltosLib.MISSING)
+                       last_main_good = tell_gonogo("main",
+                                                    state.main_voltage >= AltosLib.ao_igniter_good,
+                                                    last_main_good,
+                                                    last_tell_mode != TELL_MODE_PAD);
+
+               if (state.gps != null)
+                       last_gps_good = tell_gonogo("G P S",
+                                                   state.gps_ready,
+                                                   last_gps_good,
+                                                   last_tell_mode != TELL_MODE_PAD);
+               return true;
+       }
+
+
+       private boolean descending(int state) {
+               return AltosLib.ao_flight_drogue <= state && state <= AltosLib.ao_flight_landed;
+       }
+
+       private boolean target_moved(AltosState state) {
+               if (last_gps != null && state != null && state.gps != null) {
+                       AltosGreatCircle        moved = new AltosGreatCircle(last_gps.lat, last_gps.lon, last_gps.alt,
+                                                                            state.gps.lat, state.gps.lon, state.gps.alt);
+                       double                  height_change = 0;
+                       double                  height = state.height();
+
+                       if (height != AltosLib.MISSING && last_height != AltosLib.MISSING)
+                               height_change = Math.abs(last_height - height);
+
+                       if (moved.range < 10 && height_change < 10)
+                               return false;
                }
-               if (old_state == null || old_state.gps_ready != state.gps_ready) {
-                       if (state.gps_ready) {
-                               speak("GPS ready");
-                               spoke = true;
-                       } else if (old_state != null) {
-                               speak("GPS lost");
-                               spoke = true;
-                       }
+               return true;
+       }
+
+       private boolean receiver_moved(Location receiver) {
+               if (last_receiver != null && receiver != null) {
+                       AltosGreatCircle        moved = new AltosGreatCircle(last_receiver.getLatitude(),
+                                                                            last_receiver.getLongitude(),
+                                                                            last_receiver.getAltitude(),
+                                                                            receiver.getLatitude(),
+                                                                            receiver.getLongitude(),
+                                                                            receiver.getAltitude());
+                       if (moved.range < 10)
+                               return false;
                }
-               old_state = state;
-               if (idle_thread != null)
-                       idle_thread.notice(state, from_receiver, spoke);
+               return true;
        }
 
+       private boolean tell_flight(TelemetryState telem_state, AltosState state,
+                                   AltosGreatCircle from_receiver, Location receiver) {
 
-       class IdleThread extends Thread {
-               boolean            started;
-               private AltosState state;
-               private AltosGreatCircle from_receiver;
-               int                reported_landing;
-               int                report_interval;
-               long               report_time;
+               boolean spoken = false;
 
-               public synchronized void report(boolean last) {
-                       if (state == null)
-                               return;
+               if (state == null)
+                       return false;
 
-                       /* reset the landing count once we hear about a new flight */
-                       if (state.state < AltosLib.ao_flight_drogue)
-                               reported_landing = 0;
+               if (last_tell_mode != TELL_MODE_FLIGHT)
+                       last_flight_tell = TELL_FLIGHT_NONE;
 
-                       /* Shut up once the rocket is on the ground */
-                       if (reported_landing > 2) {
-                               return;
+               if (state.state != last_state && AltosLib.ao_flight_boost <= state.state && state.state <= AltosLib.ao_flight_landed) {
+                       speak(state.state_name());
+                       if (descending(state.state) && !descending(last_state)) {
+                               if (state.max_height() != AltosLib.MISSING) {
+                                       speak("max height: %s.",
+                                             AltosConvert.height.say_units(state.max_height()));
+                               }
                        }
+                       last_flight_tell = TELL_FLIGHT_STATE;
+                       return true;
+               }
 
-                       /* If the rocket isn't on the pad, then report location */
-                       if ((AltosLib.ao_flight_drogue <= state.state &&
-                             state.state < AltosLib.ao_flight_landed) ||
-                            state.state == AltosLib.ao_flight_stateless)
-                       {
-                               AltosGreatCircle        position;
-
-                               if (from_receiver != null)
-                                       position = from_receiver;
-                               else
-                                       position = state.from_pad;
-
-                               if (position != null) {
-                                       speak(String.format("Height %s, bearing %s %d, elevation %d, range %s.\n",
-                                                           AltosConvert.height.say_units(state.height()),
-                                                           position.bearing_words(
-                                                                   AltosGreatCircle.BEARING_VOICE),
-                                                           (int) (position.bearing + 0.5),
-                                                           (int) (position.elevation + 0.5),
-                                                           AltosConvert.distance.say_units(position.range)));
-                               }
-                       } else if (state.state > AltosLib.ao_flight_pad) {
-                               if (state.height() != AltosLib.MISSING)
-                                       speak(AltosConvert.height.say_units(state.height()));
+               if (last_tell_mode == TELL_MODE_FLIGHT && last_flight_tell == TELL_FLIGHT_TRACK) {
+                       if (time_since_speak() < 10 * 1000)
+                               return false;
+                       if (!target_moved(state) && !receiver_moved(receiver))
+                               return false;
+               }
+
+               double  speed;
+               double  height;
+
+               if (last_flight_tell == TELL_FLIGHT_NONE || last_flight_tell == TELL_FLIGHT_STATE || last_flight_tell == TELL_FLIGHT_TRACK) {
+                       last_flight_tell = TELL_FLIGHT_SPEED;
+
+                       if (state.state <= AltosLib.ao_flight_coast) {
+                               speed = state.speed();
                        } else {
-                               reported_landing = 0;
+                               speed = state.gps_speed();
+                               if (speed == AltosLib.MISSING)
+                                       speed = state.speed();
                        }
 
-                       /* 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 >= AltosLib.ao_flight_drogue &&
-                           (last ||
-                            System.currentTimeMillis() - state.received_time >= 15000 ||
-                            state.state == AltosLib.ao_flight_landed))
-                       {
-                               if (Math.abs(state.speed()) < 20 && state.height() < 100)
-                                       speak("rocket landed safely");
-                               else
-                                       speak("rocket may have crashed");
-                               if (state.from_pad != null)
-                                       speak(String.format("Bearing %d degrees, range %s.",
-                                                           (int) (state.from_pad.bearing + 0.5),
-                                                           AltosConvert.distance.say_units(state.from_pad.distance)));
-                               ++reported_landing;
+                       if (speed != AltosLib.MISSING) {
+                               speak("speed: %s.", AltosConvert.speed.say_units(speed));
+                               return true;
                        }
                }
 
-               long now () {
-                       return System.currentTimeMillis();
-               }
+               if (last_flight_tell == TELL_FLIGHT_SPEED) {
+                       last_flight_tell = TELL_FLIGHT_HEIGHT;
+                       height = state.height();
 
-               void set_report_time() {
-                       report_time = now() + report_interval;
+                       if (height != AltosLib.MISSING) {
+                               speak("height: %s.", AltosConvert.height.say_units(height));
+                               return true;
+                       }
                }
 
-               public void run () {
-                       try {
-                               for (;;) {
-                                       set_report_time();
-                                       for (;;) {
-                                               synchronized (this) {
-                                                       long sleep_time = report_time - now();
-                                                       if (sleep_time <= 0)
-                                                               break;
-                                                       wait(sleep_time);
-                                               }
-                                       }
-                                       report(false);
-                               }
-                       } catch (InterruptedException ie) {
+               if (last_flight_tell == TELL_FLIGHT_HEIGHT) {
+                       last_flight_tell = TELL_FLIGHT_TRACK;
+                       if (from_receiver != null) {
+                               speak("bearing %s %d, elevation %d, range %s.",
+                                     from_receiver.bearing_words(
+                                             AltosGreatCircle.BEARING_VOICE),
+                                     (int) (from_receiver.bearing + 0.5),
+                                     (int) (from_receiver.elevation + 0.5),
+                                     AltosConvert.distance.say(from_receiver.range));
+                               return true;
                        }
                }
 
-               public synchronized void notice(AltosState new_state, AltosGreatCircle new_from_receiver, boolean spoken) {
-                       AltosState old_state = state;
-                       state = new_state;
-                       from_receiver = new_from_receiver;
-                       if (!started && state.state > AltosLib.ao_flight_pad) {
-                               started = true;
-                               start();
-                       }
+               return spoken;
+       }
 
-                       if (state.state < AltosLib.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();
-               }
+       private boolean tell_recover(TelemetryState telem_state, AltosState state,
+                                    AltosGreatCircle from_receiver, Location receiver) {
+
+               if (from_receiver == null)
+                       return false;
 
-               public IdleThread() {
-                       state = null;
-                       reported_landing = 0;
-                       report_interval = 10000;
+               if (last_tell_mode == TELL_MODE_RECOVER) {
+                       if (!target_moved(state) && !receiver_moved(receiver))
+                               return false;
+                       if (time_since_speak() <= 10 * 1000)
+                               return false;
                }
+
+               String direction = AltosDroid.direction(from_receiver, receiver);
+               if (direction == null)
+                       direction = String.format("Bearing %d", (int) (from_receiver.bearing + 0.5));
+
+               speak("%s, range %s.", direction,
+                     AltosConvert.distance.say_units(from_receiver.distance));
+
+               return true;
        }
 
+       public void tell(TelemetryState telem_state, AltosState state,
+                        AltosGreatCircle from_receiver, Location receiver,
+                        AltosDroidTab tab) {
+
+               boolean spoken = false;
+
+               if (!tts_enabled) return;
+
+               if (is_speaking()) return;
+
+               int     tell_serial = last_tell_serial;
+
+               if (state != null)
+                       tell_serial = state.serial;
+
+               if (tell_serial != last_tell_serial)
+                       reset_last();
+
+               int     tell_mode = TELL_MODE_NONE;
+
+               if (tab.tab_name().equals(AltosDroid.tab_pad_name))
+                       tell_mode = TELL_MODE_PAD;
+               else if (tab.tab_name().equals(AltosDroid.tab_flight_name))
+                       tell_mode = TELL_MODE_FLIGHT;
+               else
+                       tell_mode = TELL_MODE_RECOVER;
+
+               if (tell_mode == TELL_MODE_PAD)
+                       spoken = tell_pad(telem_state, state, from_receiver, receiver);
+               else if (tell_mode == TELL_MODE_FLIGHT)
+                       spoken = tell_flight(telem_state, state, from_receiver, receiver);
+               else
+                       spoken = tell_recover(telem_state, state, from_receiver, receiver);
+
+               if (spoken) {
+                       last_tell_mode = tell_mode;
+                       last_tell_serial = tell_serial;
+                       if (state != null) {
+                               last_state = state.state;
+                               last_height = state.height();
+                               if (state.gps != null)
+                                       last_gps = state.gps;
+                       }
+                       if (receiver != null)
+                               last_receiver = receiver;
+               }
+       }
 }
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java b/altosdroid/src/org/altusmetrum/AltosDroid/DeviceAddress.java
new file mode 100644 (file)
index 0000000..673d72d
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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;
+
+public class DeviceAddress {
+       public String   address;
+       public String   name;
+
+       public DeviceAddress(String address, String name) {
+               this.address = address;
+               this.name = name;
+       }
+}
index 71692122b8def9d96839a9f70414d0f74ecf20e3..f36ef267c9c5c2660c5431e72638a388aa85125f 100644 (file)
@@ -27,7 +27,6 @@ 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;
@@ -45,12 +44,10 @@ import android.widget.AdapterView.OnItemClickListener;
  * 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";
+       public static final String EXTRA_DEVICE_ADDRESS = "device_address";
+       public static final String EXTRA_DEVICE_NAME = "device_name";
 
        // Member fields
        private BluetoothAdapter mBtAdapter;
@@ -136,7 +133,7 @@ public class DeviceListActivity extends Activity {
        * Start device discover with the BluetoothAdapter
        */
        private void doDiscovery() {
-               if (D) Log.d(TAG, "doDiscovery()");
+               AltosDebug.debug("doDiscovery()");
 
                // Indicate scanning in the title
                setProgressBarIndeterminateVisibility(true);
@@ -164,9 +161,20 @@ public class DeviceListActivity extends Activity {
                        String info = ((TextView) v).getText().toString();
                        String address = info.substring(info.length() - 17);
 
+                       int newline = info.indexOf('\n');
+
+                       String name = null;
+                       if (newline > 0)
+                               name = info.substring(0, newline);
+                       else
+                               name = info;
+
+                       AltosDebug.debug("******* selected item '%s'", info);
+
                        // Create the result Intent and include the MAC address
                        Intent intent = new Intent();
                        intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
+                       intent.putExtra(EXTRA_DEVICE_NAME, name);
 
                        // Set result and finish this Activity
                        setResult(Activity.RESULT_OK, intent);
@@ -183,14 +191,22 @@ public class DeviceListActivity extends Activity {
 
                        // When discovery finds a device
                        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
-                               // Get the BluetoothDevice object from the Intent
+
+                               /* 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
-                                   && device.getName().startsWith("TeleBT")               ) {
-                                       mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
+
+                               /* If it's already paired, skip it, because it's been listed already
+                                */
+                               if (device != null && device.getBondState() != BluetoothDevice.BOND_BONDED)
+                               {
+                                       String  name = device.getName();
+                                       if (name != null && name.startsWith("TeleBT"))
+                                               mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
                                }
-                       // When discovery is finished, change the Activity title
+
+                       /* When discovery is finished, change the Activity title
+                        */
                        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
                                setProgressBarIndeterminateVisibility(false);
                                setTitle(R.string.select_device);
index 267c90f8e57cc2f0df621f82959e5b9a344a4573..6cecbdf1aa9b8203b20ce33109bd0ec1cce6f136 100644 (file)
@@ -52,27 +52,14 @@ public class GoNoGoLights {
                missing = m;
                set = true;
                if (missing) {
-                       hide();
                        red.setImageDrawable(dGray);
                        green.setImageDrawable(dGray);
                } else if (state) {
                        red.setImageDrawable(dGray);
                        green.setImageDrawable(dGreen);
-                       show();
                } else {
                        red.setImageDrawable(dRed);
                        green.setImageDrawable(dGray);
-                       show();
                }
        }
-
-       public void show() {
-               red.setVisibility(View.VISIBLE);
-               green.setVisibility(View.VISIBLE);
-       }
-
-       public void hide() {
-               red.setVisibility(View.GONE);
-               green.setVisibility(View.GONE);
-       }
 }
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/MapTypeActivity.java
new file mode 100644 (file)
index 0000000..8846e56
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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.util.*;
+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.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.AdapterView.*;
+
+import org.altusmetrum.altoslib_8.*;
+
+public class MapTypeActivity extends Activity {
+       private Button hybrid;
+       private Button satellite;
+       private Button roadmap;
+       private Button terrain;
+       private int selected_type;
+
+       public static final String EXTRA_MAP_TYPE = "map_type";
+
+       private void done(int type) {
+
+               Intent intent = new Intent();
+               intent.putExtra(EXTRA_MAP_TYPE, type);
+               setResult(Activity.RESULT_OK, intent);
+               finish();
+       }
+
+       public void selectType(View view) {
+               AltosDebug.debug("selectType %s", view.toString());
+               if (view == hybrid)
+                       done(AltosMap.maptype_hybrid);
+               if (view == satellite)
+                       done(AltosMap.maptype_satellite);
+               if (view == roadmap)
+                       done(AltosMap.maptype_roadmap);
+               if (view == terrain)
+                       done(AltosMap.maptype_terrain);
+       }
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.map_type);
+
+               hybrid = (Button) findViewById(R.id.map_type_hybrid);
+               satellite = (Button) findViewById(R.id.map_type_satellite);
+               roadmap = (Button) findViewById(R.id.map_type_roadmap);
+               terrain = (Button) findViewById(R.id.map_type_terrain);
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+       }
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java b/altosdroid/src/org/altusmetrum/AltosDroid/PreloadMapActivity.java
new file mode 100644 (file)
index 0000000..d746208
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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.util.*;
+import java.io.*;
+import java.text.*;
+
+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.view.View;
+import android.view.Window;
+import android.view.View.OnClickListener;
+import android.widget.*;
+import android.widget.AdapterView.*;
+import android.location.Location;
+import android.location.LocationManager;
+import android.location.LocationListener;
+import android.location.Criteria;
+
+import org.altusmetrum.altoslib_8.*;
+
+/**
+ * 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 PreloadMapActivity extends Activity implements AltosLaunchSiteListener, AltosMapInterface, AltosMapLoaderListener, LocationListener {
+
+       private ArrayAdapter<AltosLaunchSite> known_sites_adapter;
+
+       private CheckBox        hybrid;
+       private CheckBox        satellite;
+       private CheckBox        roadmap;
+       private CheckBox        terrain;
+
+       private Spinner         known_sites_spinner;
+       private Spinner         min_zoom;
+       private Spinner         max_zoom;
+       private TextView        radius_label;
+       private Spinner         radius;
+
+       private EditText        latitude;
+       private EditText        longitude;
+
+       private ProgressBar     progress;
+
+       /* AltosMapLoaderListener interfaces */
+       public void loader_start(final int max) {
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       progress.setMax(max);
+                                       progress.setProgress(0);
+                               }
+                       });
+       }
+
+       public void loader_notify(final int cur, final int max, final String name) {
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       progress.setProgress(cur);
+                               }
+                       });
+       }
+
+       public void loader_done(int max) {
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       progress.setProgress(0);
+                                       finish();
+                               }
+                       });
+       }
+
+       /* AltosLaunchSiteListener interface */
+       public void notify_launch_sites(final List<AltosLaunchSite> sites) {
+               this.runOnUiThread(new Runnable() {
+                               public void run() {
+                                       for (AltosLaunchSite site : sites)
+                                               known_sites_adapter.add(site);
+                               }
+                       });
+       }
+
+       AltosMap        map;
+       AltosMapLoader  loader;
+
+       class PreloadMapImage implements AltosImage {
+               public void flush() {
+               }
+
+               public PreloadMapImage(File file) {
+               }
+       }
+
+       public AltosMapPath new_path() {
+               return null;
+       }
+
+       public AltosMapLine new_line() {
+               return null;
+       }
+
+       public AltosImage load_image(File file) throws Exception {
+               return new PreloadMapImage(file);
+       }
+
+       public AltosMapMark new_mark(double lat, double lon, int state) {
+               return null;
+       }
+
+       class PreloadMapTile extends AltosMapTile {
+               public void paint(AltosMapTransform t) {
+               }
+
+               public PreloadMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+                       super(listener, upper_left, center, zoom, maptype, px_size, 2);
+               }
+
+       }
+
+       public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+               return new PreloadMapTile(listener, upper_left, center, zoom, maptype, px_size);
+       }
+
+       public int width() {
+               return AltosMap.px_size;
+       }
+
+       public int height() {
+               return AltosMap.px_size;
+       }
+
+       public void repaint() {
+       }
+
+       public void repaint(AltosRectangle damage) {
+       }
+
+       public void set_zoom_label(String label) {
+       }
+
+       public void select_object(AltosLatLon latlon) {
+       }
+
+       public void debug(String format, Object ... arguments) {
+               AltosDebug.debug(format, arguments);
+       }
+
+       /* LocationProvider interface */
+
+       AltosLaunchSite current_location_site;
+
+       public void onLocationChanged(Location location) {
+               AltosDebug.debug("location changed");
+               if (current_location_site == null) {
+                       AltosLaunchSite selected_item = (AltosLaunchSite) known_sites_spinner.getSelectedItem();
+
+                       current_location_site = new AltosLaunchSite("Current Location", location.getLatitude(), location.getLongitude());
+                       known_sites_adapter.insert(current_location_site, 0);
+
+                       if (selected_item != null)
+                               known_sites_spinner.setSelection(known_sites_adapter.getPosition(selected_item));
+                       else {
+                               latitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.latitude)));
+                               longitude.setText(new StringBuffer(String.format("%12.6f", current_location_site.longitude)));
+                       }
+               } else {
+                       current_location_site.latitude = location.getLatitude();
+                       current_location_site.longitude = location.getLongitude();
+               }
+       }
+
+       public void onStatusChanged(String provider, int status, Bundle extras) {
+       }
+
+       public void onProviderEnabled(String provider) {
+       }
+
+       public void onProviderDisabled(String provider) {
+       }
+
+       private double text(EditText view) throws ParseException {
+               return AltosParse.parse_double_locale(view.getEditableText().toString());
+       }
+
+       private double latitude() throws ParseException {
+               return text(latitude);
+       }
+
+       private double longitude() throws ParseException {
+               return text(longitude);
+       }
+
+       private int value(Spinner spinner) {
+               return (Integer) spinner.getSelectedItem();
+       }
+
+       private int min_z() {
+               return value(min_zoom);
+       }
+
+       private int max_z() {
+               return value(max_zoom);
+       }
+
+       private double value_distance(Spinner spinner) {
+               return (Double) spinner.getSelectedItem();
+       }
+
+       private double radius() {
+               double r = value_distance(radius);
+               if (AltosPreferences.imperial_units())
+                       r = AltosConvert.distance.inverse(r);
+               else
+                       r = r * 1000;
+               return r;
+       }
+
+       private int bit(CheckBox box, int value) {
+               if (box.isChecked())
+                       return 1 << value;
+               return 0;
+       }
+
+       private int types() {
+               return (bit(hybrid, AltosMap.maptype_hybrid) |
+                       bit(satellite, AltosMap.maptype_satellite) |
+                       bit(roadmap, AltosMap.maptype_roadmap) |
+                       bit(terrain, AltosMap.maptype_terrain));
+       }
+
+       private void load() {
+               try {
+                       double  lat = latitude();
+                       double  lon = longitude();
+                       int     min = min_z();
+                       int     max = max_z();
+                       double  r = radius();
+                       int     t = types();
+
+                       AltosDebug.debug("PreloadMap load %f %f %d %d %f %d\n",
+                                        lat, lon, min, max, r, t);
+                       loader.load(lat, lon, min, max, r, t);
+               } catch (ParseException e) {
+                       AltosDebug.debug("PreloadMap load raised exception %s", e.toString());
+               }
+       }
+
+       private void add_numbers(Spinner spinner, int min, int max, int def) {
+
+               ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(this, android.R.layout.simple_spinner_item);
+
+               int     spinner_def = 0;
+               int     pos = 0;
+
+               for (int i = min; i <= max; i++) {
+                       adapter.add(new Integer(i));
+                       if (i == def)
+                               spinner_def = pos;
+                       pos++;
+               }
+
+               spinner.setAdapter(adapter);
+               spinner.setSelection(spinner_def);
+       }
+
+
+       private void add_distance(Spinner spinner, double[] distances_km, double def_km, double[] distances_mi, double def_mi) {
+
+               ArrayAdapter<Double> adapter = new ArrayAdapter<Double>(this, android.R.layout.simple_spinner_item);
+
+               int     spinner_def = 0;
+               int     pos = 0;
+
+               double[] distances;
+               double  def;
+               if (AltosPreferences.imperial_units()) {
+                       distances = distances_mi;
+                       def = def_mi;
+               } else {
+                       distances = distances_km;
+                       def = def_km;
+               }
+
+               for (int i = 0; i < distances.length; i++) {
+                       adapter.add(distances[i]);
+                       if (distances[i] == def)
+                               spinner_def = pos;
+                       pos++;
+               }
+
+               spinner.setAdapter(adapter);
+               spinner.setSelection(spinner_def);
+       }
+
+
+
+       class SiteListListener implements OnItemSelectedListener {
+               public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+                       AltosLaunchSite site = (AltosLaunchSite) parent.getItemAtPosition(pos);
+                       latitude.setText(new StringBuffer(String.format("%12.6f", site.latitude)));
+                       longitude.setText(new StringBuffer(String.format("%12.6f", site.longitude)));
+               }
+               public void onNothingSelected(AdapterView<?> parent) {
+               }
+
+               public SiteListListener() {
+               }
+       }
+
+       double[]        radius_mi = { 1, 2, 5, 10, 20 };
+       double          radius_def_mi = 2;
+       double[]        radius_km = { 1, 2, 5, 10, 20, 30 };
+       double          radius_def_km = 2;
+
+       @Override
+       protected void onCreate(Bundle savedInstanceState) {
+               super.onCreate(savedInstanceState);
+
+               // Setup the window
+               requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
+               setContentView(R.layout.map_preload);
+
+               // Set result CANCELED incase the user backs out
+               setResult(Activity.RESULT_CANCELED);
+
+               // Initialize the button to perform device discovery
+               Button loadButton = (Button) findViewById(R.id.preload_load);
+               loadButton.setOnClickListener(new OnClickListener() {
+                       public void onClick(View v) {
+                               load();
+                       }
+               });
+
+               latitude = (EditText) findViewById(R.id.preload_latitude);
+               longitude = (EditText) findViewById(R.id.preload_longitude);
+
+               hybrid = (CheckBox) findViewById(R.id.preload_hybrid);
+               satellite = (CheckBox) findViewById(R.id.preload_satellite);
+               roadmap = (CheckBox) findViewById(R.id.preload_roadmap);
+               terrain = (CheckBox) findViewById(R.id.preload_terrain);
+
+               hybrid.setChecked(true);
+
+               min_zoom = (Spinner) findViewById(R.id.preload_min_zoom);
+               add_numbers(min_zoom,
+                           AltosMap.min_zoom - AltosMap.default_zoom,
+                           AltosMap.max_zoom - AltosMap.default_zoom, -2);
+               max_zoom = (Spinner) findViewById(R.id.preload_max_zoom);
+               add_numbers(max_zoom,
+                           AltosMap.min_zoom - AltosMap.default_zoom,
+                           AltosMap.max_zoom - AltosMap.default_zoom, 2);
+               radius_label = (TextView) findViewById(R.id.preload_radius_label);
+               radius = (Spinner) findViewById(R.id.preload_radius);
+               if (AltosPreferences.imperial_units())
+                       radius_label.setText("Radius (miles)");
+               else
+                       radius_label.setText("Radius (km)");
+               add_distance(radius, radius_km, radius_def_km, radius_mi, radius_def_mi);
+
+               progress = (ProgressBar) findViewById(R.id.preload_progress);
+
+               // Initialize array adapters. One for already paired devices and
+               // one for newly discovered devices
+               known_sites_spinner = (Spinner) findViewById(R.id.preload_site_list);
+
+               known_sites_adapter = new ArrayAdapter<AltosLaunchSite>(this, android.R.layout.simple_spinner_item);
+
+               known_sites_adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+               known_sites_spinner.setAdapter(known_sites_adapter);
+               known_sites_spinner.setOnItemSelectedListener(new SiteListListener());
+
+               map = new AltosMap(this);
+
+               loader = new AltosMapLoader(map, this);
+
+               // Listen for GPS and Network position updates
+               LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+
+               locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
+
+               new AltosLaunchSites(this);
+       }
+
+       @Override
+       protected void onDestroy() {
+               super.onDestroy();
+
+               // Stop listening for location updates
+               ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
+       }
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabAscent.java
deleted file mode 100644 (file)
index 23de962..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright © 2013 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 org.altusmetrum.altoslib_6.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.location.Location;
-
-public class TabAscent extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
-       private TextView mHeightView;
-       private TextView mMaxHeightView;
-       private TextView mSpeedView;
-       private TextView mMaxSpeedView;
-       private TextView mAccelView;
-       private TextView mMaxAccelView;
-       private TextView mLatitudeView;
-       private TextView mLongitudeView;
-       private TextView mApogeeVoltageView;
-       private GoNoGoLights mApogeeLights;
-       private TextView mMainVoltageView;
-       private GoNoGoLights mMainLights;
-
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
-
-       @Override
-       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               View v = inflater.inflate(R.layout.tab_ascent, container, false);
-
-               mHeightView    = (TextView) v.findViewById(R.id.height_value);
-               mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
-               mSpeedView     = (TextView) v.findViewById(R.id.speed_value);
-               mMaxSpeedView  = (TextView) v.findViewById(R.id.max_speed_value);
-               mAccelView     = (TextView) v.findViewById(R.id.accel_value);
-               mMaxAccelView  = (TextView) v.findViewById(R.id.max_accel_value);
-               mLatitudeView  = (TextView) v.findViewById(R.id.lat_value);
-               mLongitudeView = (TextView) v.findViewById(R.id.lon_value);
-
-               mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
-               mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
-                                                (ImageView) v.findViewById(R.id.apogee_greenled),
-                                                getResources());
-
-               mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
-               mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
-                                              (ImageView) v.findViewById(R.id.main_greenled),
-                                              getResources());
-
-               return v;
-       }
-
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-       }
-
-       public String tab_name() {
-               return "ascent";
-       }
-
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               if (state != null) {
-                       set_value(mHeightView, AltosConvert.height, 6, state.height());
-                       set_value(mHeightView, AltosConvert.height, 6, state.height());
-                       set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
-                       set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
-                       set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
-                       set_value(mAccelView, AltosConvert.accel, 6, state.acceleration());
-                       set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
-
-                       if (state.gps != null) {
-                               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-                               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
-                       } else {
-                               mLatitudeView.setText("");
-                               mLongitudeView.setText("");
-                       }
-
-                       mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
-                       mApogeeLights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
-
-                       mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
-                       mMainLights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
-               }
-       }
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabDescent.java
deleted file mode 100644 (file)
index 4ec6f40..0000000
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright © 2013 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 org.altusmetrum.altoslib_6.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.location.Location;
-
-public class TabDescent extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
-       private TextView mSpeedView;
-       private TextView mHeightView;
-       private TextView mElevationView;
-       private TextView mRangeView;
-       private TextView mBearingView;
-       private TextView mCompassView;
-       private TextView mDistanceView;
-       private TextView mLatitudeView;
-       private TextView mLongitudeView;
-       private TextView mApogeeVoltageView;
-       private GoNoGoLights mApogeeLights;
-       private TextView mMainVoltageView;
-       private GoNoGoLights mMainLights;
-
-
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
-
-       @Override
-       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               View v = inflater.inflate(R.layout.tab_descent, container, false);
-
-               mSpeedView     = (TextView) v.findViewById(R.id.speed_value);
-               mHeightView    = (TextView) v.findViewById(R.id.height_value);
-               mElevationView = (TextView) v.findViewById(R.id.elevation_value);
-               mRangeView     = (TextView) v.findViewById(R.id.range_value);
-               mBearingView   = (TextView) v.findViewById(R.id.bearing_value);
-               mCompassView   = (TextView) v.findViewById(R.id.compass_value);
-               mDistanceView  = (TextView) v.findViewById(R.id.distance_value);
-               mLatitudeView  = (TextView) v.findViewById(R.id.lat_value);
-               mLongitudeView = (TextView) v.findViewById(R.id.lon_value);
-
-               mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
-               mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
-                                                (ImageView) v.findViewById(R.id.apogee_greenled),
-                                                getResources());
-
-               mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
-               mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
-                                              (ImageView) v.findViewById(R.id.main_greenled),
-                                              getResources());
-
-               return v;
-       }
-
-
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-       }
-
-       public String tab_name() { return "descent"; }
-
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               if (state != null) {
-                       set_value(mSpeedView, AltosConvert.speed, 6, state.speed());
-                       set_value(mHeightView, AltosConvert.height, 6, state.height());
-                       if (from_receiver != null) {
-                               mElevationView.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
-                               set_value(mRangeView, AltosConvert.distance, 6, from_receiver.range);
-                               mBearingView.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
-                               mCompassView.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
-                               set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
-                       } else { 
-                               mElevationView.setText("<unknown>");
-                               mRangeView.setText("<unknown>");
-                               mBearingView.setText("<unknown>");
-                               mCompassView.setText("<unknown>");
-                               mDistanceView.setText("<unknown>");
-                       }
-                       if (state.gps != null) {
-                               mLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-                               mLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
-                       }
-
-                       mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
-                       mApogeeLights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
-
-                       mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
-                       mMainLights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
-               }
-       }
-
-}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabFlight.java
new file mode 100644 (file)
index 0000000..a503f1b
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright © 2013 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 org.altusmetrum.altoslib_8.*;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.*;
+import android.widget.*;
+import android.location.Location;
+
+public class TabFlight extends AltosDroidTab {
+       private TextView speed_view;
+       private TextView height_view;
+       private TextView max_speed_view;
+       private TextView max_height_view;
+       private TextView elevation_view;
+       private TextView range_view;
+       private TextView bearing_view;
+       private TextView compass_view;
+       private TextView distance_view;
+       private TextView latitude_view;
+       private TextView longitude_view;
+       private View apogee_view;
+       private TextView apogee_voltage_view;
+       private TextView apogee_voltage_label;
+       private GoNoGoLights apogee_lights;
+       private View main_view;
+       private TextView main_voltage_view;
+       private TextView main_voltage_label;
+       private GoNoGoLights main_lights;
+
+       @Override
+       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+               View v = inflater.inflate(R.layout.tab_flight, container, false);
+
+               speed_view     = (TextView) v.findViewById(R.id.speed_value);
+               height_view    = (TextView) v.findViewById(R.id.height_value);
+               max_speed_view = (TextView) v.findViewById(R.id.max_speed_value);
+               max_height_view= (TextView) v.findViewById(R.id.max_height_value);
+               elevation_view = (TextView) v.findViewById(R.id.elevation_value);
+               range_view     = (TextView) v.findViewById(R.id.range_value);
+               bearing_view   = (TextView) v.findViewById(R.id.bearing_value);
+               compass_view   = (TextView) v.findViewById(R.id.compass_value);
+               distance_view  = (TextView) v.findViewById(R.id.distance_value);
+               latitude_view  = (TextView) v.findViewById(R.id.lat_value);
+               longitude_view = (TextView) v.findViewById(R.id.lon_value);
+
+               apogee_view = v.findViewById(R.id.apogee_view);
+               apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
+               apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
+                                                (ImageView) v.findViewById(R.id.apogee_greenled),
+                                                getResources());
+               apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
+
+               main_view = v.findViewById(R.id.main_view);
+               main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
+               main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
+                                              (ImageView) v.findViewById(R.id.main_greenled),
+                                              getResources());
+               main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
+
+               return v;
+       }
+
+       public String tab_name() { return AltosDroid.tab_flight_name; }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (state != null) {
+                       set_value(speed_view, AltosConvert.speed, 6, state.speed());
+                       set_value(height_view, AltosConvert.height, 6, state.height());
+                       set_value(max_speed_view, AltosConvert.speed, 6, state.max_speed());
+                       set_value(max_height_view, AltosConvert.speed, 6, state.max_height());
+                       if (from_receiver != null) {
+                               elevation_view.setText(AltosDroid.number("%3.0f°", from_receiver.elevation));
+                               set_value(range_view, AltosConvert.distance, 6, from_receiver.range);
+                               bearing_view.setText(AltosDroid.number("%3.0f°", from_receiver.bearing));
+                               compass_view.setText(from_receiver.bearing_words(AltosGreatCircle.BEARING_LONG));
+                               set_value(distance_view, AltosConvert.distance, 6, from_receiver.distance);
+                       } else { 
+                               elevation_view.setText("<unknown>");
+                               range_view.setText("<unknown>");
+                               bearing_view.setText("<unknown>");
+                               compass_view.setText("<unknown>");
+                               distance_view.setText("<unknown>");
+                       }
+                       if (state.gps != null) {
+                               latitude_view.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                               longitude_view.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
+                       }
+
+                       if (state.apogee_voltage == AltosLib.MISSING) {
+                               apogee_view.setVisibility(View.GONE);
+                       } else {
+                               apogee_voltage_view.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
+                               apogee_lights.set(state.apogee_voltage > 3.2, state.apogee_voltage == AltosLib.MISSING);
+                               apogee_view.setVisibility(View.VISIBLE);
+                       }
+
+                       if (state.main_voltage == AltosLib.MISSING) {
+                               main_view.setVisibility(View.GONE);
+                       } else {
+                               main_voltage_view.setText(AltosDroid.number("%4.2f V", state.main_voltage));
+                               main_lights.set(state.main_voltage > 3.2, state.main_voltage == AltosLib.MISSING);
+                               main_view.setVisibility(View.VISIBLE);
+                       }
+               }
+       }
+
+}
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabLanded.java
deleted file mode 100644 (file)
index 4c69d86..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright © 2013 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 org.altusmetrum.altoslib_6.*;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-import android.location.Location;
-
-public class TabLanded extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
-       private TextView mBearingView;
-       private TextView mDistanceView;
-       private TextView mTargetLatitudeView;
-       private TextView mTargetLongitudeView;
-       private TextView mReceiverLatitudeView;
-       private TextView mReceiverLongitudeView;
-       private TextView mMaxHeightView;
-       private TextView mMaxSpeedView;
-       private TextView mMaxAccelView;
-
-
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
-
-       @Override
-       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               View v = inflater.inflate(R.layout.tab_landed, container, false);
-
-               mBearingView   = (TextView) v.findViewById(R.id.bearing_value);
-               mDistanceView  = (TextView) v.findViewById(R.id.distance_value);
-               mTargetLatitudeView  = (TextView) v.findViewById(R.id.target_lat_value);
-               mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
-               mReceiverLatitudeView  = (TextView) v.findViewById(R.id.receiver_lat_value);
-               mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
-               mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
-               mMaxSpeedView  = (TextView) v.findViewById(R.id.max_speed_value);
-               mMaxAccelView  = (TextView) v.findViewById(R.id.max_accel_value);
-
-               return v;
-       }
-
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-       }
-
-       public String tab_name() { return "landed"; }
-
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
-               if (from_receiver != null) {
-                       mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
-                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
-               }
-               if (state != null && state.gps != null) {
-                       mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
-                       mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
-               }
-
-               if (receiver != null) {
-                       mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
-                       mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
-               }
-
-               if (state != null) {
-                       set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
-                       set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
-                       set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
-               }
-       }
-}
index 871b94a1bb57703a1a81bc32426ff97e045939b9..54ccd18f7e95e5ab5f8bd8e1fb5b4d61e616c424 100644 (file)
 
 package org.altusmetrum.AltosDroid;
 
-import java.util.Arrays;
+import java.util.*;
+import java.io.*;
 
-import org.altusmetrum.altoslib_6.*;
-
-import com.google.android.gms.maps.CameraUpdateFactory;
-import com.google.android.gms.maps.GoogleMap;
-import com.google.android.gms.maps.SupportMapFragment;
-import com.google.android.gms.maps.model.BitmapDescriptorFactory;
-import com.google.android.gms.maps.model.LatLng;
-import com.google.android.gms.maps.model.Marker;
-import com.google.android.gms.maps.model.MarkerOptions;
-import com.google.android.gms.maps.model.Polyline;
-import com.google.android.gms.maps.model.PolylineOptions;
+import org.altusmetrum.altoslib_8.*;
 
 import android.app.Activity;
-import android.graphics.Color;
+import android.graphics.*;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
-//import android.support.v4.app.FragmentTransaction;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
+import android.support.v4.app.FragmentTransaction;
+import android.view.*;
+import android.widget.*;
 import android.location.Location;
+import android.content.*;
 
 public class TabMap extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
-       private SupportMapFragment mMapFragment;
-       private GoogleMap mMap;
-       private boolean mapLoaded = false;
 
-       private Marker mRocketMarker;
-       private Marker mPadMarker;
-       private boolean pad_set;
-       private Polyline mPolyline;
+       AltosLatLon     here;
 
        private TextView mDistanceView;
+       private TextView mBearingLabel;
        private TextView mBearingView;
        private TextView mTargetLatitudeView;
        private TextView mTargetLongitudeView;
        private TextView mReceiverLatitudeView;
        private TextView mReceiverLongitudeView;
-
-       private double mapAccuracy = -1;
+       private AltosMapOffline map_offline;
+       private AltosMapOnline map_online;
+       private View view;
+       private int map_source;
 
        @Override
        public void onAttach(Activity activity) {
                super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
        }
 
        @Override
        public void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
-
-               mMapFragment = new SupportMapFragment() {
-                       @Override
-                       public void onActivityCreated(Bundle savedInstanceState) {
-                               super.onActivityCreated(savedInstanceState);
-                               setupMap();
-                       }
-               };
-
        }
 
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-               View v = inflater.inflate(R.layout.tab_map, container, false);
-               mDistanceView  = (TextView)v.findViewById(R.id.distance_value);
-               mBearingView   = (TextView)v.findViewById(R.id.bearing_value);
-               mTargetLatitudeView  = (TextView)v.findViewById(R.id.target_lat_value);
-               mTargetLongitudeView = (TextView)v.findViewById(R.id.target_lon_value);
-               mReceiverLatitudeView  = (TextView)v.findViewById(R.id.receiver_lat_value);
-               mReceiverLongitudeView = (TextView)v.findViewById(R.id.receiver_lon_value);
-               return v;
+               view = inflater.inflate(R.layout.tab_map, container, false);
+               int map_source = AltosDroidPreferences.map_source();
+
+               mDistanceView  = (TextView)view.findViewById(R.id.distance_value);
+               mBearingLabel  = (TextView)view.findViewById(R.id.bearing_label);
+               mBearingView   = (TextView)view.findViewById(R.id.bearing_value);
+               mTargetLatitudeView  = (TextView)view.findViewById(R.id.target_lat_value);
+               mTargetLongitudeView = (TextView)view.findViewById(R.id.target_lon_value);
+               mReceiverLatitudeView  = (TextView)view.findViewById(R.id.receiver_lat_value);
+               mReceiverLongitudeView = (TextView)view.findViewById(R.id.receiver_lon_value);
+               map_offline = (AltosMapOffline)view.findViewById(R.id.map_offline);
+               map_offline.onCreateView(altos_droid);
+               map_online = new AltosMapOnline(view.getContext());
+               map_online.onCreateView(altos_droid);
+               set_map_source(AltosDroidPreferences.map_source());
+               return view;
        }
 
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
                super.onActivityCreated(savedInstanceState);
-               getChildFragmentManager().beginTransaction().add(R.id.map, mMapFragment).commit();
+               if (map_online != null)
+                       getChildFragmentManager().beginTransaction().add(R.id.map_online, map_online.mMapFragment).commit();
        }
 
        @Override
        public void onDestroyView() {
                super.onDestroyView();
-
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
-
-               //Fragment fragment = (getFragmentManager().findFragmentById(R.id.map));
-               //FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
-               //ft.remove(fragment);
-               //ft.commit();
        }
 
-       private void setupMap() {
-               mMap = mMapFragment.getMap();
-               if (mMap != null) {
-                       mMap.setMyLocationEnabled(true);
-                       mMap.getUiSettings().setTiltGesturesEnabled(false);
-                       mMap.getUiSettings().setZoomControlsEnabled(false);
-
-                       mRocketMarker = mMap.addMarker(
-                                       // From: http://mapicons.nicolasmollet.com/markers/industry/military/missile-2/
-                                       new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.rocket))
-                                                          .position(new LatLng(0,0))
-                                                          .visible(false)
-                                       );
-
-                       mPadMarker = mMap.addMarker(
-                                       new MarkerOptions().icon(BitmapDescriptorFactory.fromResource(R.drawable.pad))
-                                                          .position(new LatLng(0,0))
-                                                          .visible(false)
-                                       );
-
-                       mPolyline = mMap.addPolyline(
-                                       new PolylineOptions().add(new LatLng(0,0), new LatLng(0,0))
-                                                            .width(3)
-                                                            .color(Color.BLUE)
-                                                            .visible(false)
-                                       );
-
-                       mapLoaded = true;
-               }
-       }
+       public String tab_name() { return AltosDroid.tab_map_name; }
 
        private void center(double lat, double lon, double accuracy) {
-               if (mapAccuracy < 0 || accuracy < mapAccuracy/10) {
-                       mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lon),14));
-                       mapAccuracy = accuracy;
-               }
+               if (map_offline != null)
+                       map_offline.center(lat, lon, accuracy);
+               if (map_online != null)
+                       map_online.center(lat, lon, accuracy);
        }
 
-       public String tab_name() { return "map"; }
-
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (from_receiver != null) {
-                       mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+                       String  direction = AltosDroid.direction(from_receiver, receiver);
+                       if (direction != null) {
+                               mBearingLabel.setText("Direction");
+                               mBearingView.setText(direction);
+                       } else {
+                               mBearingLabel.setText("Bearing");
+                               mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+                       }
                        set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
+               } else {
+                       mBearingLabel.setText("Bearing");
+                       mBearingView.setText("");
+                       set_value(mDistanceView, AltosConvert.distance, 6, AltosLib.MISSING);
                }
 
                if (state != null) {
-                       if (mapLoaded) {
-                               if (state.gps != null) {
-                                       mRocketMarker.setPosition(new LatLng(state.gps.lat, state.gps.lon));
-                                       mRocketMarker.setVisible(true);
-
-                                       mPolyline.setPoints(Arrays.asList(new LatLng(state.pad_lat, state.pad_lon), new LatLng(state.gps.lat, state.gps.lon)));
-                                       mPolyline.setVisible(true);
-                               }
-
-                               if (!pad_set && state.pad_lat != AltosLib.MISSING) {
-                                       pad_set = true;
-                                       mPadMarker.setPosition(new LatLng(state.pad_lat, state.pad_lon));
-                                       mPadMarker.setVisible(true);
-                               }
-                       }
                        if (state.gps != null) {
                                mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
                                mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
-                               if (state.gps.locked && state.gps.nsat >= 4)
-                                       center (state.gps.lat, state.gps.lon, 10);
                        }
                }
 
                if (receiver != null) {
                        double accuracy;
 
+                       here = new AltosLatLon(receiver.getLatitude(), receiver.getLongitude());
                        if (receiver.hasAccuracy())
                                accuracy = receiver.getAccuracy();
                        else
                                accuracy = 1000;
-                       mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
-                       mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
-                       center (receiver.getLatitude(), receiver.getLongitude(), accuracy);
+                       mReceiverLatitudeView.setText(AltosDroid.pos(here.lat, "N", "S"));
+                       mReceiverLongitudeView.setText(AltosDroid.pos(here.lon, "E", "W"));
+                       center (here.lat, here.lon, accuracy);
+               }
+               if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
+                       if (map_offline != null)
+                               map_offline.show(telem_state, state, from_receiver, receiver);
+               } else {
+                       if (map_online != null)
+                               map_online.show(telem_state, state, from_receiver, receiver);
                }
+       }
 
+       @Override
+       public void set_map_type(int map_type) {
+               if (map_offline != null)
+                       map_offline.set_map_type(map_type);
+               if (map_online != null)
+                       map_online.set_map_type(map_type);
+       }
+
+       @Override
+       public void set_map_source(int map_source) {
+               this.map_source = map_source;
+               if (map_source == AltosDroidPreferences.MAP_SOURCE_OFFLINE) {
+                       if (map_online != null)
+                               map_online.set_visible(false);
+                       if (map_offline != null) {
+                               map_offline.set_visible(true);
+                               map_offline.show(last_telem_state, last_state, last_from_receiver, last_receiver);
+                       }
+               } else {
+                       if (map_offline != null)
+                               map_offline.set_visible(false);
+                       if (map_online != null) {
+                               map_online.set_visible(true);
+                               map_online.show(last_telem_state, last_state, last_from_receiver, last_receiver);
+                       }
+               }
        }
 
        public TabMap() {
index 0ac78219bf1f047c0deae317c9bb7be72e5ae866..4d04316f6f626c7eb6b09f4d64a7f3bd8fbbc048 100644 (file)
 
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import android.app.Activity;
 import android.os.Bundle;
 import android.support.v4.app.Fragment;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
+import android.view.*;
+import android.widget.*;
 import android.location.Location;
 
 public class TabPad extends AltosDroidTab {
-       AltosDroid mAltosDroid;
-
-       private TextView mBatteryVoltageView;
-       private TextView mBatteryVoltageLabel;
-       private GoNoGoLights mBatteryLights;
-       private TextView mApogeeVoltageView;
-       private TextView mApogeeVoltageLabel;
-       private GoNoGoLights mApogeeLights;
-       private TextView mMainVoltageView;
-       private TextView mMainVoltageLabel;
-       private GoNoGoLights mMainLights;
-       private TextView mDataLoggingView;
-       private GoNoGoLights mDataLoggingLights;
-       private TextView mGPSLockedView;
-       private GoNoGoLights mGPSLockedLights;
-       private TextView mGPSReadyView;
-       private GoNoGoLights mGPSReadyLights;
-       private TextView mPadLatitudeView;
-       private TextView mPadLongitudeView;
-       private TextView mPadAltitudeView;
+       private TextView battery_voltage_view;
+       private GoNoGoLights battery_lights;
+
+       private TableRow receiver_row;
+       private TextView receiver_voltage_view;
+       private TextView receiver_voltage_label;
+       private GoNoGoLights receiver_voltage_lights;
+
+       private TableRow apogee_row;
+       private TextView apogee_voltage_view;
+       private TextView apogee_voltage_label;
+       private GoNoGoLights apogee_lights;
+
+       private TableRow main_row;
+       private TextView main_voltage_view;
+       private TextView main_voltage_label;
+       private GoNoGoLights main_lights;
+
+       private TextView data_logging_view;
+       private GoNoGoLights data_logging_lights;
+
+       private TextView gps_locked_view;
+       private GoNoGoLights gps_locked_lights;
+
+       private TextView gps_ready_view;
+       private GoNoGoLights gps_ready_lights;
+
+       private TextView receiver_latitude_view;
+       private TextView receiver_longitude_view;
+       private TextView receiver_altitude_view;
+
+       private TableRow[] ignite_row = new TableRow[4];
+       private TextView[] ignite_voltage_view = new TextView[4];
+       private TextView[] ignite_voltage_label = new TextView[4];
+       private GoNoGoLights[] ignite_lights = new GoNoGoLights[4];
 
-       @Override
-       public void onAttach(Activity activity) {
-               super.onAttach(activity);
-               mAltosDroid = (AltosDroid) activity;
-               mAltosDroid.registerTab(this);
-       }
 
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                View v = inflater.inflate(R.layout.tab_pad, container, false);
-               mBatteryVoltageView = (TextView) v.findViewById(R.id.battery_voltage_value);
-               mBatteryVoltageLabel = (TextView) v.findViewById(R.id.battery_voltage_label);
-               mBatteryLights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
+               battery_voltage_view = (TextView) v.findViewById(R.id.battery_voltage_value);
+               battery_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.battery_redled),
                                                  (ImageView) v.findViewById(R.id.battery_greenled),
                                                  getResources());
 
-               mApogeeVoltageView = (TextView) v.findViewById(R.id.apogee_voltage_value);
-               mApogeeVoltageLabel = (TextView) v.findViewById(R.id.apogee_voltage_label);
-               mApogeeLights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
+               receiver_row = (TableRow) v.findViewById(R.id.receiver_row);
+               receiver_voltage_view = (TextView) v.findViewById(R.id.receiver_voltage_value);
+               receiver_voltage_label = (TextView) v.findViewById(R.id.receiver_voltage_label);
+               receiver_voltage_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.receiver_redled),
+                                                          (ImageView) v.findViewById(R.id.receiver_greenled),
+                                                          getResources());
+
+               apogee_row = (TableRow) v.findViewById(R.id.apogee_row);
+               apogee_voltage_view = (TextView) v.findViewById(R.id.apogee_voltage_value);
+               apogee_voltage_label = (TextView) v.findViewById(R.id.apogee_voltage_label);
+               apogee_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.apogee_redled),
                                                 (ImageView) v.findViewById(R.id.apogee_greenled),
                                                 getResources());
 
-               mMainVoltageView = (TextView) v.findViewById(R.id.main_voltage_value);
-               mMainVoltageLabel = (TextView) v.findViewById(R.id.main_voltage_label);
-               mMainLights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
+               main_row = (TableRow) v.findViewById(R.id.main_row);
+               main_voltage_view = (TextView) v.findViewById(R.id.main_voltage_value);
+               main_voltage_label = (TextView) v.findViewById(R.id.main_voltage_label);
+               main_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.main_redled),
                                               (ImageView) v.findViewById(R.id.main_greenled),
                                               getResources());
 
-               mDataLoggingView = (TextView) v.findViewById(R.id.logging_value);
-               mDataLoggingLights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
+               data_logging_view = (TextView) v.findViewById(R.id.logging_value);
+               data_logging_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.logging_redled),
                                                      (ImageView) v.findViewById(R.id.logging_greenled),
                                                      getResources());
 
-               mGPSLockedView = (TextView) v.findViewById(R.id.gps_locked_value);
-               mGPSLockedLights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
+               gps_locked_view = (TextView) v.findViewById(R.id.gps_locked_value);
+               gps_locked_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_locked_redled),
                                                    (ImageView) v.findViewById(R.id.gps_locked_greenled),
                                                    getResources());
 
-               mGPSReadyView = (TextView) v.findViewById(R.id.gps_ready_value);
-               mGPSReadyLights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
+               gps_ready_view = (TextView) v.findViewById(R.id.gps_ready_value);
+               gps_ready_lights = new GoNoGoLights((ImageView) v.findViewById(R.id.gps_ready_redled),
                                                   (ImageView) v.findViewById(R.id.gps_ready_greenled),
                                                   getResources());
 
-               mPadLatitudeView = (TextView) v.findViewById(R.id.pad_lat_value);
-               mPadLongitudeView = (TextView) v.findViewById(R.id.pad_lon_value);
-               mPadAltitudeView = (TextView) v.findViewById(R.id.pad_alt_value);
-        return v;
-       }
+               for (int i = 0; i < 4; i++) {
+                       int row_id, view_id, label_id, lights_id;
+                       int red_id, green_id;
+                       switch (i) {
+                       case 0:
+                       default:
+                               row_id = R.id.ignite_a_row;
+                               view_id = R.id.ignite_a_voltage_value;
+                               label_id = R.id.ignite_a_voltage_label;
+                               red_id = R.id.ignite_a_redled;
+                               green_id = R.id.ignite_a_greenled;
+                               break;
+                       case 1:
+                               row_id = R.id.ignite_b_row;
+                               view_id = R.id.ignite_b_voltage_value;
+                               label_id = R.id.ignite_b_voltage_label;
+                               red_id = R.id.ignite_b_redled;
+                               green_id = R.id.ignite_b_greenled;
+                               break;
+                       case 2:
+                               row_id = R.id.ignite_c_row;
+                               view_id = R.id.ignite_c_voltage_value;
+                               label_id = R.id.ignite_c_voltage_label;
+                               red_id = R.id.ignite_c_redled;
+                               green_id = R.id.ignite_c_greenled;
+                               break;
+                       case 3:
+                               row_id = R.id.ignite_d_row;
+                               view_id = R.id.ignite_d_voltage_value;
+                               label_id = R.id.ignite_d_voltage_label;
+                               red_id = R.id.ignite_d_redled;
+                               green_id = R.id.ignite_d_greenled;
+                               break;
+                       }
+                       ignite_row[i] = (TableRow) v.findViewById(row_id);
+                       ignite_voltage_view[i] = (TextView) v.findViewById(view_id);
+                       ignite_voltage_label[i] = (TextView) v.findViewById(label_id);
+                       ignite_lights[i] = new GoNoGoLights((ImageView) v.findViewById(red_id),
+                                                            (ImageView) v.findViewById(green_id),
+                                                            getResources());
+               }
 
-       @Override
-       public void onDestroy() {
-               super.onDestroy();
-               mAltosDroid.unregisterTab(this);
-               mAltosDroid = null;
+               receiver_latitude_view = (TextView) v.findViewById(R.id.receiver_lat_value);
+               receiver_longitude_view = (TextView) v.findViewById(R.id.receiver_lon_value);
+               receiver_altitude_view = (TextView) v.findViewById(R.id.receiver_alt_value);
+        return v;
        }
 
-       public String tab_name() { return "pad"; }
+       public String tab_name() { return AltosDroid.tab_pad_name; }
 
-       public void show(AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
                if (state != null) {
-                       mBatteryVoltageView.setText(AltosDroid.number("%4.2f V", state.battery_voltage));
-                       mBatteryLights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
+                       battery_voltage_view.setText(AltosDroid.number(" %4.2f V", state.battery_voltage));
+                       battery_lights.set(state.battery_voltage >= AltosLib.ao_battery_good, state.battery_voltage == AltosLib.MISSING);
                        if (state.apogee_voltage == AltosLib.MISSING) {
-                               mApogeeVoltageView.setVisibility(View.GONE);
-                               mApogeeVoltageLabel.setVisibility(View.GONE);
+                               apogee_row.setVisibility(View.GONE);
                        } else {
-                               mApogeeVoltageView.setText(AltosDroid.number("%4.2f V", state.apogee_voltage));
-                               mApogeeVoltageView.setVisibility(View.VISIBLE);
-                               mApogeeVoltageLabel.setVisibility(View.VISIBLE);
+                               apogee_voltage_view.setText(AltosDroid.number(" %4.2f V", state.apogee_voltage));
+                               apogee_row.setVisibility(View.VISIBLE);
                        }
-                       mApogeeLights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
+                       apogee_lights.set(state.apogee_voltage >= AltosLib.ao_igniter_good, state.apogee_voltage == AltosLib.MISSING);
                        if (state.main_voltage == AltosLib.MISSING) {
-                               mMainVoltageView.setVisibility(View.GONE);
-                               mMainVoltageLabel.setVisibility(View.GONE);
+                               main_row.setVisibility(View.GONE);
                        } else {
-                               mMainVoltageView.setText(AltosDroid.number("%4.2f V", state.main_voltage));
-                               mMainVoltageView.setVisibility(View.VISIBLE);
-                               mMainVoltageLabel.setVisibility(View.VISIBLE);
+                               main_voltage_view.setText(AltosDroid.number(" %4.2f V", state.main_voltage));
+                               main_row.setVisibility(View.VISIBLE);
+                       }
+                       main_lights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
+
+                       int num_igniter = state.ignitor_voltage == null ? 0 : state.ignitor_voltage.length;
+
+                       for (int i = 0; i < 4; i++) {
+                               double voltage = i >= num_igniter ? AltosLib.MISSING : state.ignitor_voltage[i];
+                               if (voltage == AltosLib.MISSING) {
+                                       ignite_row[i].setVisibility(View.GONE);
+                               } else {
+                                       ignite_voltage_view[i].setText(AltosDroid.number(" %4.2f V", voltage));
+                                       ignite_row[i].setVisibility(View.VISIBLE);
+                               }
+                               ignite_lights[i].set(voltage >= AltosLib.ao_igniter_good, voltage == AltosLib.MISSING);
                        }
-                       mMainLights.set(state.main_voltage >= AltosLib.ao_igniter_good, state.main_voltage == AltosLib.MISSING);
 
                        if (state.flight != 0) {
                                if (state.state <= AltosLib.ao_flight_pad)
-                                       mDataLoggingView.setText("Ready to record");
+                                       data_logging_view.setText("Ready to record");
                                else if (state.state < AltosLib.ao_flight_landed)
-                                       mDataLoggingView.setText("Recording data");
+                                       data_logging_view.setText("Recording data");
                                else
-                                       mDataLoggingView.setText("Recorded data");
+                                       data_logging_view.setText("Recorded data");
                        } else {
-                               mDataLoggingView.setText("Storage full");
+                               data_logging_view.setText("Storage full");
                        }
-                       mDataLoggingLights.set(state.flight != 0, state.flight == AltosLib.MISSING);
+                       data_logging_lights.set(state.flight != 0, state.flight == AltosLib.MISSING);
 
                        if (state.gps != null) {
                                int soln = state.gps.nsat;
                                int nsat = state.gps.cc_gps_sat != null ? state.gps.cc_gps_sat.length : 0;
-                               mGPSLockedView.setText(String.format("%4d in soln, %4d in view", soln, nsat));
-                               mGPSLockedLights.set(state.gps.locked && state.gps.nsat >= 4, false);
+                               gps_locked_view.setText(String.format("%d in soln, %d in view", soln, nsat));
+                               gps_locked_lights.set(state.gps.locked && state.gps.nsat >= 4, false);
                                if (state.gps_ready)
-                                       mGPSReadyView.setText("Ready");
+                                       gps_ready_view.setText("Ready");
                                else
-                                       mGPSReadyView.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
+                                       gps_ready_view.setText(AltosDroid.integer("Waiting %d", state.gps_waiting));
                        } else
-                               mGPSLockedLights.set(false, true);
-                       mGPSReadyLights.set(state.gps_ready, state.gps == null);
+                               gps_locked_lights.set(false, true);
+                       gps_ready_lights.set(state.gps_ready, state.gps == null);
+               }
+
+               if (telem_state != null) {
+                       if (telem_state.receiver_battery == AltosLib.MISSING) {
+                               receiver_row.setVisibility(View.GONE);
+                       } else {
+                               receiver_voltage_view.setText(AltosDroid.number(" %4.2f V", telem_state.receiver_battery));
+                               receiver_row.setVisibility(View.VISIBLE);
+                       }
+                       receiver_voltage_lights.set(telem_state.receiver_battery >= AltosLib.ao_battery_good, telem_state.receiver_battery == AltosLib.MISSING);
                }
 
                if (receiver != null) {
                        double altitude = AltosLib.MISSING;
                        if (receiver.hasAltitude())
                                altitude = receiver.getAltitude();
-                       mPadLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
-                       mPadLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
-                       set_value(mPadAltitudeView, AltosConvert.height, 6, altitude);
+                       receiver_latitude_view.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+                       receiver_longitude_view.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
+                       set_value(receiver_altitude_view, AltosConvert.height, 1, altitude);
                }
        }
-
 }
diff --git a/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java b/altosdroid/src/org/altusmetrum/AltosDroid/TabRecover.java
new file mode 100644 (file)
index 0000000..19bb79d
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2013 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 org.altusmetrum.altoslib_8.*;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.location.Location;
+
+public class TabRecover extends AltosDroidTab {
+       private TextView mBearingView;
+       private TextView mDirectionView;
+       private TextView mDistanceView;
+       private TextView mTargetLatitudeView;
+       private TextView mTargetLongitudeView;
+       private TextView mReceiverLatitudeView;
+       private TextView mReceiverLongitudeView;
+       private TextView mMaxHeightView;
+       private TextView mMaxSpeedView;
+       private TextView mMaxAccelView;
+
+       @Override
+       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+               View v = inflater.inflate(R.layout.tab_recover, container, false);
+
+               mBearingView   = (TextView) v.findViewById(R.id.bearing_value);
+               mDirectionView = (TextView) v.findViewById(R.id.direction_value);
+               mDistanceView  = (TextView) v.findViewById(R.id.distance_value);
+               mTargetLatitudeView  = (TextView) v.findViewById(R.id.target_lat_value);
+               mTargetLongitudeView = (TextView) v.findViewById(R.id.target_lon_value);
+               mReceiverLatitudeView  = (TextView) v.findViewById(R.id.receiver_lat_value);
+               mReceiverLongitudeView = (TextView) v.findViewById(R.id.receiver_lon_value);
+               mMaxHeightView = (TextView) v.findViewById(R.id.max_height_value);
+               mMaxSpeedView  = (TextView) v.findViewById(R.id.max_speed_value);
+               mMaxAccelView  = (TextView) v.findViewById(R.id.max_accel_value);
+
+               return v;
+       }
+
+       public String tab_name() { return AltosDroid.tab_recover_name; }
+
+       public void show(TelemetryState telem_state, AltosState state, AltosGreatCircle from_receiver, Location receiver) {
+               if (from_receiver != null) {
+                       mBearingView.setText(String.format("%3.0f°", from_receiver.bearing));
+                       set_value(mDistanceView, AltosConvert.distance, 6, from_receiver.distance);
+                       String direction = AltosDroid.direction(from_receiver, receiver);
+                       if (direction == null)
+                               mDirectionView.setText("");
+                       else
+                               mDirectionView.setText(direction);
+               }
+               if (state != null && state.gps != null) {
+                       mTargetLatitudeView.setText(AltosDroid.pos(state.gps.lat, "N", "S"));
+                       mTargetLongitudeView.setText(AltosDroid.pos(state.gps.lon, "E", "W"));
+               }
+
+               if (receiver != null) {
+                       mReceiverLatitudeView.setText(AltosDroid.pos(receiver.getLatitude(), "N", "S"));
+                       mReceiverLongitudeView.setText(AltosDroid.pos(receiver.getLongitude(), "E", "W"));
+               }
+
+               if (state != null) {
+                       set_value(mMaxHeightView, AltosConvert.height, 6, state.max_height());
+                       set_value(mMaxAccelView, AltosConvert.accel, 6, state.max_acceleration());
+                       set_value(mMaxSpeedView, AltosConvert.speed, 6, state.max_speed());
+               }
+       }
+}
index 1ac34f9d5590ea5a2c0b8a16b030b9a9df5ba8fe..b34a25b60c8a3973cd8fc1640be2655df41b2010 100644 (file)
@@ -29,7 +29,6 @@ import android.view.View;
 import android.view.ViewGroup;
 import android.widget.TabHost;
 import android.widget.TabWidget;
-import android.util.Log;
 
 /**
  * This is a helper class that implements the management of tabs and all
@@ -106,7 +105,7 @@ public class TabsAdapter extends FragmentPagerAdapter
        @Override
        public Fragment getItem(int position) {
                TabInfo info = mTabs.get(position);
-               Log.d(AltosDroid.TAG, String.format("TabsAdapter.getItem(%d)", position));
+               AltosDebug.debug("TabsAdapter.getItem(%d)", position);
                info.fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
                return info.fragment;
        }
@@ -131,7 +130,7 @@ public class TabsAdapter extends FragmentPagerAdapter
                if (cur_frag != null) {
                        cur_frag.set_visible(true);
                }
-               Log.d(AltosDroid.TAG, String.format("TabsAdapter.onTabChanged(%s) = %d", tabId, position));
+               AltosDebug.debug("TabsAdapter.onTabChanged(%s) = %d", tabId, position);
                mViewPager.setCurrentItem(position);
        }
 
index 9764ab72c2f62f78630d45cb0df0021a8c7e278b..79020c16f4859083bbe6c1564d2caee87e7b4cf7 100644 (file)
@@ -1,18 +1,14 @@
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Environment;
-import android.util.Log;
 
 public class TelemetryLogger {
-       private static final String TAG = "TelemetryLogger";
-       private static final boolean D = true;
-
        private Context   context = null;
        private AltosLink link    = null;
        private AltosLog  logger  = null;
@@ -33,21 +29,21 @@ public class TelemetryLogger {
 
        private void close() {
                if (logger != null) {
-                       if (D) Log.d(TAG, "Shutting down Telemetry Logging");
+                       AltosDebug.debug("Shutting down Telemetry Logging");
                        logger.close();
                        logger = null;
                }
        }
-       
+
        void handleExternalStorageState() {
                String state = Environment.getExternalStorageState();
                if (Environment.MEDIA_MOUNTED.equals(state)) {
                        if (logger == null) {
-                               if (D) Log.d(TAG, "Starting up Telemetry Logging");
+                               AltosDebug.debug("Starting up Telemetry Logging");
                                logger = new AltosLog(link);
                        }
                } else {
-                       if (D) Log.d(TAG, "External Storage not present - stopping");
+                       AltosDebug.debug("External Storage not present - stopping");
                        close();
                }
        }
index 4e4408d5edc0c44b5865f2a0520c8d84e23f246c..3199f25238477c9261361a957615b767cd741d2b 100644 (file)
@@ -21,42 +21,32 @@ package org.altusmetrum.AltosDroid;
 
 import java.text.*;
 import java.io.*;
+import java.util.*;
 import java.util.concurrent.*;
-import android.util.Log;
 import android.os.Handler;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 
 public class TelemetryReader extends Thread {
 
-       private static final String TAG = "TelemetryReader";
-       private static final boolean D = true;
-
        int         crc_errors;
 
        Handler     handler;
 
        AltosLink   link;
-       AltosState  state = null;
 
        LinkedBlockingQueue<AltosLine> telemQueue;
 
-       public AltosState read() throws ParseException, AltosCRCException, InterruptedException, IOException {
+       public AltosTelemetry read() throws ParseException, AltosCRCException, InterruptedException, IOException {
                AltosLine l = telemQueue.take();
                if (l.line == null)
                        throw new IOException("IO error");
                AltosTelemetry telem = AltosTelemetryLegacy.parse(l.line);
-               if (state == null)
-                       state = new AltosState();
-               else
-                       state = state.clone();
-               telem.update_state(state);
-               return state;
+               return telem;
        }
 
        public void close() {
-               state = null;
                link.remove_monitor(telemQueue);
                link = null;
                telemQueue.clear();
@@ -64,16 +54,14 @@ public class TelemetryReader extends Thread {
        }
 
        public void run() {
-               AltosState  state = null;
-
                try {
-                       if (D) Log.d(TAG, "starting loop");
+                       AltosDebug.debug("starting loop");
                        while (telemQueue != null) {
                                try {
-                                       state = read();
-                                       handler.obtainMessage(TelemetryService.MSG_TELEMETRY, state).sendToTarget();
+                                       AltosTelemetry  telem = read();
+                                       handler.obtainMessage(TelemetryService.MSG_TELEMETRY, telem).sendToTarget();
                                } catch (ParseException pp) {
-                                       Log.e(TAG, String.format("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage()));
+                                       AltosDebug.error("Parse error: %d \"%s\"", pp.getErrorOffset(), pp.getMessage());
                                } catch (AltosCRCException ce) {
                                        ++crc_errors;
                                        handler.obtainMessage(TelemetryService.MSG_CRC_ERROR, new Integer(crc_errors)).sendToTarget();
@@ -81,21 +69,22 @@ public class TelemetryReader extends Thread {
                        }
                } catch (InterruptedException ee) {
                } catch (IOException ie) {
+                       AltosDebug.error("IO exception in telemetry reader");
+                       handler.obtainMessage(TelemetryService.MSG_DISCONNECTED, link).sendToTarget();
                } finally {
                        close();
                }
        }
 
-       public TelemetryReader (AltosLink in_link, Handler in_handler, AltosState in_state) {
-               if (D) Log.d(TAG, "connected TelemetryReader create started");
+       public TelemetryReader (AltosLink in_link, Handler in_handler) {
+               AltosDebug.debug("connected TelemetryReader create started");
                link    = in_link;
                handler = in_handler;
 
-               state = in_state;
                telemQueue = new LinkedBlockingQueue<AltosLine>();
                link.add_monitor(telemQueue);
                link.set_telemetry(AltosLib.ao_telemetry_standard);
 
-               if (D) Log.d(TAG, "connected TelemetryReader created");
+               AltosDebug.debug("connected TelemetryReader created");
        }
 }
index d4ac66aabe76ba7c1168e556f249196267f451da..4a056d9550812f27c885cbfce3d1e059361bb344 100644 (file)
 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 java.util.*;
 
 import android.app.Notification;
 //import android.app.NotificationManager;
@@ -29,6 +27,7 @@ import android.app.PendingIntent;
 import android.app.Service;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothAdapter;
+import android.hardware.usb.*;
 import android.content.Intent;
 import android.content.Context;
 import android.os.Bundle;
@@ -38,55 +37,50 @@ import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.Looper;
-import android.util.Log;
 import android.widget.Toast;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationListener;
 import android.location.Criteria;
 
-import org.altusmetrum.altoslib_6.*;
-
+import org.altusmetrum.altoslib_8.*;
 
 public class TelemetryService extends Service implements LocationListener {
 
-       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;
-       static final int MSG_CRC_ERROR         = 9;
-       static final int MSG_SETBAUD           = 10;
+       static final int MSG_OPEN_USB          = 4;
+       static final int MSG_CONNECTED         = 5;
+       static final int MSG_CONNECT_FAILED    = 6;
+       static final int MSG_DISCONNECTED      = 7;
+       static final int MSG_TELEMETRY         = 8;
+       static final int MSG_SETFREQUENCY      = 9;
+       static final int MSG_CRC_ERROR         = 10;
+       static final int MSG_SETBAUD           = 11;
+       static final int MSG_DISCONNECT        = 12;
+       static final int MSG_DELETE_SERIAL     = 13;
 
        // 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.
+       ArrayList<Messenger> clients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
+       final Handler   handler   = new IncomingHandler(this);
+       final Messenger messenger = new Messenger(handler); // Target we publish for clients to send messages to IncomingHandler.
 
        // Name of the connected device
-       String address;
-       private AltosBluetooth  mAltosBluetooth  = null;
-       private TelemetryReader mTelemetryReader = null;
-       private TelemetryLogger mTelemetryLogger = null;
-       // Local Bluetooth adapter
-       private BluetoothAdapter mBluetoothAdapter = null;
+       DeviceAddress address;
+       private AltosDroidLink  altos_link  = null;
+       private TelemetryReader telemetry_reader = null;
+       private TelemetryLogger telemetry_logger = null;
 
-       private TelemetryState  telemetry_state;
+       // Local Bluetooth adapter
+       private BluetoothAdapter bluetooth_adapter = null;
 
        // Last data seen; send to UI when it starts
+       private TelemetryState  telemetry_state;
 
        // Handler of incoming messages from clients.
        static class IncomingHandler extends Handler {
@@ -96,83 +90,121 @@ public class TelemetryService extends Service implements LocationListener {
                @Override
                public void handleMessage(Message msg) {
                        TelemetryService s = service.get();
+                       AltosDroidLink bt = null;
                        if (s == null)
                                return;
                        switch (msg.what) {
+
+                               /* Messages from application */
                        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
-                                       msg.replyTo.send(s.message());
-                               } catch (RemoteException e) {
-                                       s.mClients.remove(msg.replyTo);
-                               }
-                               if (D) Log.d(TAG, "Client bound to service");
+                               s.add_client(msg.replyTo);
                                break;
                        case MSG_UNREGISTER_CLIENT:
-                               s.mClients.remove(msg.replyTo);
-                               if (D) Log.d(TAG, "Client unbound from service");
+                               s.remove_client(msg.replyTo);
                                break;
                        case MSG_CONNECT:
-                               if (D) Log.d(TAG, "Connect command received");
-                               String address = (String) msg.obj;
+                               AltosDebug.debug("Connect command received");
+                               DeviceAddress address = (DeviceAddress) msg.obj;
                                AltosDroidPreferences.set_active_device(address);
-                               s.startAltosBluetooth(address);
-                               break;
-                       case MSG_CONNECTED:
-                               if (D) Log.d(TAG, "Connected to device");
-                               try {
-                                       s.connected();
-                               } catch (InterruptedException ie) {
-                               }
+                               s.start_altos_bluetooth(address, false);
                                break;
-                       case MSG_CONNECT_FAILED:
-                               if (D) Log.d(TAG, "Connection failed... retrying");
-                               if (s.address != null)
-                                       s.startAltosBluetooth(s.address);
+                       case MSG_OPEN_USB:
+                               AltosDebug.debug("Open USB command received");
+                               UsbDevice device = (UsbDevice) msg.obj;
+                               s.start_usb(device);
                                break;
-                       case MSG_DISCONNECTED:
-                               Log.d(TAG, "MSG_DISCONNECTED");
-                               s.stopAltosBluetooth();
+                       case MSG_DISCONNECT:
+                               AltosDebug.debug("Disconnect command received");
+                               s.address = null;
+                               s.disconnect(true);
                                break;
-                       case MSG_TELEMETRY:
-                               // forward telemetry messages
-                               s.telemetry_state.state = (AltosState) msg.obj;
-                               if (s.telemetry_state.state != null) {
-                                       if (D) Log.d(TAG, "Save state");
-                                       AltosPreferences.set_state(0, s.telemetry_state.state, null);
-                               }
-                               if (D) Log.d(TAG, "MSG_TELEMETRY");
-                               s.sendMessageToClients();
-                               break;
-                       case MSG_CRC_ERROR:
-                               // forward crc error messages
-                               s.telemetry_state.crc_errors = (Integer) msg.obj;
-                               if (D) Log.d(TAG, "MSG_CRC_ERROR");
-                               s.sendMessageToClients();
+                       case MSG_DELETE_SERIAL:
+                               AltosDebug.debug("Delete Serial command received");
+                               s.delete_serial((Integer) msg.obj);
                                break;
                        case MSG_SETFREQUENCY:
-                               if (D) Log.d(TAG, "MSG_SETFREQUENCY");
+                               AltosDebug.debug("MSG_SETFREQUENCY");
                                s.telemetry_state.frequency = (Double) msg.obj;
                                if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
                                        try {
-                                               s.mAltosBluetooth.set_radio_frequency(s.telemetry_state.frequency);
-                                               s.mAltosBluetooth.save_frequency();
+                                               s.altos_link.set_radio_frequency(s.telemetry_state.frequency);
+                                               s.altos_link.save_frequency();
                                        } catch (InterruptedException e) {
                                        } catch (TimeoutException e) {
                                        }
                                }
-                               s.sendMessageToClients();
+                               s.send_to_clients();
                                break;
                        case MSG_SETBAUD:
-                               if (D) Log.d(TAG, "MSG_SETBAUD");
+                               AltosDebug.debug("MSG_SETBAUD");
                                s.telemetry_state.telemetry_rate = (Integer) msg.obj;
                                if (s.telemetry_state.connect == TelemetryState.CONNECT_CONNECTED) {
-                                       s.mAltosBluetooth.set_telemetry_rate(s.telemetry_state.telemetry_rate);
-                                       s.mAltosBluetooth.save_telemetry_rate();
+                                       s.altos_link.set_telemetry_rate(s.telemetry_state.telemetry_rate);
+                                       s.altos_link.save_telemetry_rate();
+                               }
+                               s.send_to_clients();
+                               break;
+
+                               /*
+                                *Messages from AltosBluetooth
+                                */
+                       case MSG_CONNECTED:
+                               AltosDebug.debug("MSG_CONNECTED");
+                               bt = (AltosDroidLink) msg.obj;
+
+                               if (bt != s.altos_link) {
+                                       AltosDebug.debug("Stale message");
+                                       break;
+                               }
+                               AltosDebug.debug("Connected to device");
+                               try {
+                                       s.connected();
+                               } catch (InterruptedException ie) {
+                               }
+                               break;
+                       case MSG_CONNECT_FAILED:
+                               AltosDebug.debug("MSG_CONNECT_FAILED");
+                               bt = (AltosDroidLink) msg.obj;
+
+                               if (bt != s.altos_link) {
+                                       AltosDebug.debug("Stale message");
+                                       break;
+                               }
+                               if (s.address != null) {
+                                       AltosDebug.debug("Connection failed... retrying");
+                                       s.start_altos_bluetooth(s.address, true);
+                               } else {
+                                       s.disconnect(true);
                                }
-                               s.sendMessageToClients();
+                               break;
+                       case MSG_DISCONNECTED:
+
+                               /* This can be sent by either AltosDroidLink or TelemetryReader */
+                               AltosDebug.debug("MSG_DISCONNECTED");
+                               bt = (AltosDroidLink) msg.obj;
+
+                               if (bt != s.altos_link) {
+                                       AltosDebug.debug("Stale message");
+                                       break;
+                               }
+                               if (s.address != null) {
+                                       AltosDebug.debug("Connection lost... retrying");
+                                       s.start_altos_bluetooth(s.address, true);
+                               } else {
+                                       s.disconnect(true);
+                               }
+                               break;
+
+                               /*
+                                * Messages from TelemetryReader
+                                */
+                       case MSG_TELEMETRY:
+                               s.telemetry((AltosTelemetry) msg.obj);
+                               break;
+                       case MSG_CRC_ERROR:
+                               // forward crc error messages
+                               s.telemetry_state.crc_errors = (Integer) msg.obj;
+                               s.send_to_clients();
                                break;
                        default:
                                super.handleMessage(msg);
@@ -180,161 +212,288 @@ public class TelemetryService extends Service implements LocationListener {
                }
        }
 
+       /* Handle telemetry packet
+        */
+       private void telemetry(AltosTelemetry telem) {
+               AltosState      state;
+
+               if (telemetry_state.states.containsKey(telem.serial))
+                       state = telemetry_state.states.get(telem.serial).clone();
+               else
+                       state = new AltosState();
+               telem.update_state(state);
+               telemetry_state.states.put(telem.serial, state);
+               if (state != null) {
+                       AltosPreferences.set_state(telem.serial, state, null);
+               }
+               send_to_clients();
+       }
+
+       /* Construct the message to deliver to clients
+        */
        private Message message() {
                if (telemetry_state == null)
-                       Log.d(TAG, "telemetry_state null!");
-               if (telemetry_state.state == null)
-                       Log.d(TAG, "telemetry_state.state null!");
+                       AltosDebug.debug("telemetry_state null!");
+               if (telemetry_state.states == null)
+                       AltosDebug.debug("telemetry_state.states null!");
                return Message.obtain(null, AltosDroid.MSG_STATE, telemetry_state);
        }
 
-       private void sendMessageToClients() {
-               Message m = message();
-               if (D) Log.d(TAG, String.format("Send message to %d clients", mClients.size()));
-               for (int i=mClients.size()-1; i>=0; i--) {
-                       try {
-                               if (D) Log.d(TAG, String.format("Send message to client %d", i));
-                               mClients.get(i).send(m);
-                       } catch (RemoteException e) {
-                               mClients.remove(i);
-                       }
+       /* A new friend has connected
+        */
+       private void add_client(Messenger client) {
+
+               clients.add(client);
+               AltosDebug.debug("Client bound to service");
+
+               /* On connect, send the current state to the new client
+                */
+               send_to_client(client, message());
+
+               /* If we've got an address from a previous session, then
+                * go ahead and try to reconnect to the device
+                */
+               if (address != null && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
+                       AltosDebug.debug("Reconnecting now...");
+                       start_altos_bluetooth(address, false);
                }
        }
 
-       private void stopAltosBluetooth() {
-               if (D) Log.d(TAG, "stopAltosBluetooth(): begin");
-               telemetry_state.connect = TelemetryState.CONNECT_READY;
-               if (mTelemetryReader != null) {
-                       if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryReader");
-                       mTelemetryReader.interrupt();
+       /* A client has disconnected, clean up
+        */
+       private void remove_client(Messenger client) {
+               clients.remove(client);
+               AltosDebug.debug("Client unbound from service");
+
+               /* When the list of clients is empty, stop the service if
+                * we have no current telemetry source
+                */
+
+                if (clients.isEmpty() && telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED) {
+                        AltosDebug.debug("No clients, no connection. Stopping\n");
+                        stopSelf();
+                }
+       }
+
+       private void send_to_client(Messenger client, Message m) {
+               try {
+                       client.send(m);
+               } catch (RemoteException e) {
+                       AltosDebug.error("Client %s disappeared", client.toString());
+                       remove_client(client);
+               }
+       }
+
+       private void send_to_clients() {
+               Message m = message();
+               for (Messenger client : clients)
+                       send_to_client(client, m);
+       }
+
+       private void disconnect(boolean notify) {
+               AltosDebug.debug("disconnect(): begin");
+
+               telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
+               telemetry_state.address = null;
+
+               if (altos_link != null)
+                       altos_link.closing();
+
+               stop_receiver_voltage_timer();
+
+               if (telemetry_reader != null) {
+                       AltosDebug.debug("disconnect(): stopping TelemetryReader");
+                       telemetry_reader.interrupt();
                        try {
-                               mTelemetryReader.join();
+                               telemetry_reader.join();
                        } catch (InterruptedException e) {
                        }
-                       mTelemetryReader = null;
+                       telemetry_reader = null;
                }
-               if (mTelemetryLogger != null) {
-                       if (D) Log.d(TAG, "stopAltosBluetooth(): stopping TelemetryLogger");
-                       mTelemetryLogger.stop();
-                       mTelemetryLogger = null;
+               if (telemetry_logger != null) {
+                       AltosDebug.debug("disconnect(): stopping TelemetryLogger");
+                       telemetry_logger.stop();
+                       telemetry_logger = null;
                }
-               if (mAltosBluetooth != null) {
-                       if (D) Log.d(TAG, "stopAltosBluetooth(): stopping AltosBluetooth");
-                       mAltosBluetooth.close();
-                       mAltosBluetooth = null;
+               if (altos_link != null) {
+                       AltosDebug.debug("disconnect(): stopping AltosDroidLink");
+                       altos_link.close();
+                       altos_link = null;
                }
                telemetry_state.config = null;
-               if (D) Log.d(TAG, "stopAltosBluetooth(): send message to clients");
-               sendMessageToClients();
+               if (notify) {
+                       AltosDebug.debug("disconnect(): send message to clients");
+                       send_to_clients();
+                       if (clients.isEmpty()) {
+                               AltosDebug.debug("disconnect(): no clients, terminating");
+                               stopSelf();
+                       }
+               }
        }
 
-       private void startAltosBluetooth(String address) {
+       private void start_usb(UsbDevice device) {
+               AltosUsb        d = new AltosUsb(this, device, handler);
+
+               if (d != null) {
+                       disconnect(false);
+                       altos_link = d;
+                       try {
+                               connected();
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       private void delete_serial(int serial) {
+               telemetry_state.states.remove((Integer) serial);
+               AltosPreferences.remove_state(serial);
+               send_to_clients();
+       }
+
+       private void start_altos_bluetooth(DeviceAddress address, boolean pause) {
                // Get the BLuetoothDevice object
-               BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
+               BluetoothDevice device = bluetooth_adapter.getRemoteDevice(address.address);
 
+               disconnect(false);
                this.address = address;
-               if (mAltosBluetooth == null) {
-                       if (D) Log.d(TAG, String.format("startAltosBluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress()));
-                       mAltosBluetooth = new AltosBluetooth(device, mHandler);
-                       telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
-                       sendMessageToClients();
-               } 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, address), 3000);
-                       stopAltosBluetooth();
+               AltosDebug.debug("start_altos_bluetooth(): Connecting to %s (%s)", device.getName(), device.getAddress());
+               altos_link = new AltosBluetooth(device, handler, pause);
+               telemetry_state.connect = TelemetryState.CONNECT_CONNECTING;
+               telemetry_state.address = address;
+               send_to_clients();
+       }
+
+       // Timer for receiver battery voltage monitoring
+       Timer receiver_voltage_timer;
+
+       private void update_receiver_voltage() {
+               if (altos_link != null) {
+                       try {
+                               double  voltage = altos_link.monitor_battery();
+                               telemetry_state.receiver_battery = voltage;
+                       } catch (InterruptedException ie) {
+                       }
+               }
+       }
+
+       private void stop_receiver_voltage_timer() {
+               if (receiver_voltage_timer != null) {
+                       receiver_voltage_timer.cancel();
+                       receiver_voltage_timer.purge();
+                       receiver_voltage_timer = null;
+               }
+       }
+
+       private void start_receiver_voltage_timer() {
+               if (receiver_voltage_timer == null && altos_link.has_monitor_battery()) {
+                       receiver_voltage_timer = new Timer();
+                       receiver_voltage_timer.scheduleAtFixedRate(new TimerTask() { public void run() {update_receiver_voltage();}}, 1000L, 10000L);
                }
        }
 
        private void connected() throws InterruptedException {
-               if (D) Log.d(TAG, "connected top");
+               AltosDebug.debug("connected top");
+               AltosDebug.check_ui("connected\n");
                try {
-                       if (mAltosBluetooth == null)
+                       if (altos_link == null)
                                throw new InterruptedException("no bluetooth");
-                       telemetry_state.config = mAltosBluetooth.config_data();
-                       mAltosBluetooth.set_radio_frequency(telemetry_state.frequency);
-                       mAltosBluetooth.set_telemetry_rate(telemetry_state.telemetry_rate);
+                       telemetry_state.config = altos_link.config_data();
+                       altos_link.set_radio_frequency(telemetry_state.frequency);
+                       altos_link.set_telemetry_rate(telemetry_state.telemetry_rate);
                } 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();
+                       AltosDebug.debug("connected timeout");
+                       if (address != null) {
+                               AltosDebug.debug("connected timeout, retrying");
+                               start_altos_bluetooth(address, true);
+                       } else {
+                               handler.obtainMessage(MSG_CONNECT_FAILED).sendToTarget();
+                               disconnect(true);
+                       }
                        return;
                }
 
-               if (D) Log.d(TAG, "connected bluetooth configured");
+               AltosDebug.debug("connected bluetooth configured");
                telemetry_state.connect = TelemetryState.CONNECT_CONNECTED;
+               telemetry_state.address = address;
 
-               mTelemetryReader = new TelemetryReader(mAltosBluetooth, mHandler, telemetry_state.state);
-               mTelemetryReader.start();
+               telemetry_reader = new TelemetryReader(altos_link, handler);
+               telemetry_reader.start();
 
-               if (D) Log.d(TAG, "connected TelemetryReader started");
+               AltosDebug.debug("connected TelemetryReader started");
 
-               mTelemetryLogger = new TelemetryLogger(this, mAltosBluetooth);
+               telemetry_logger = new TelemetryLogger(this, altos_link);
 
-               if (D) Log.d(TAG, "Notify UI of connection");
+               start_receiver_voltage_timer();
 
-               sendMessageToClients();
-       }
+               AltosDebug.debug("Notify UI of connection");
 
-       private void onTimerTick() {
-               if (D) Log.d(TAG, "Timer wakeup");
-               try {
-                       if (mClients.size() <= 0 && telemetry_state.connect != TelemetryState.CONNECT_CONNECTED) {
-                               stopSelf();
-                       }
-               } catch (Throwable t) {
-                       Log.e(TAG, "Timer failed: ", t);
-               }
+               send_to_clients();
        }
 
 
        @Override
        public void onCreate() {
+
+               AltosDebug.init(this);
+
+               // Initialise preferences
+               AltosDroidPreferences.init(this);
+
                // Get local Bluetooth adapter
-               mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+               bluetooth_adapter = BluetoothAdapter.getDefaultAdapter();
 
                // If the adapter is null, then Bluetooth is not supported
-               if (mBluetoothAdapter == null) {
+               if (bluetooth_adapter == null) {
                        Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
                }
 
-               // Initialise preferences
-               AltosDroidPreferences.init(this);
-
                telemetry_state = new TelemetryState();
 
                // Create a reference to the NotificationManager so that we can update our notifcation text later
                //mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
 
-               telemetry_state.connect = TelemetryState.CONNECT_READY;
+               telemetry_state.connect = TelemetryState.CONNECT_DISCONNECTED;
+               telemetry_state.address = null;
 
-               AltosSavedState saved_state = AltosPreferences.state(0);
+               /* Pull the saved state information out of the preferences database
+                */
+               ArrayList<Integer> serials = AltosPreferences.list_states();
 
-               if (saved_state != null) {
-                       if (D) Log.d(TAG, String.format("recovered old state flight %d\n", saved_state.state.flight));
-                       telemetry_state.state = saved_state.state;
-               }
+               telemetry_state.latest_serial = AltosPreferences.latest_state();
 
-               // Start our timer - first event in 10 seconds, then every 10 seconds after that.
-               timer.scheduleAtFixedRate(new TimerTask(){ public void run() {onTimerTick();}}, 10000L, 10000L);
+               for (int serial : serials) {
+                       AltosSavedState saved_state = AltosPreferences.state(serial);
+                       if (saved_state != null) {
+                               if (serial == 0) {
+                                       serial = saved_state.state.serial;
+                                       AltosPreferences.set_state(serial, saved_state.state, saved_state.listener_state);
+                                       AltosPreferences.remove_state(0);
+                               }
+                               if (telemetry_state.latest_serial == 0)
+                                       telemetry_state.latest_serial = serial;
+
+                               AltosDebug.debug("recovered old state serial %d flight %d\n",
+                                                serial,
+                                                saved_state.state.flight);
+                               if (saved_state.state.gps != null)
+                                       AltosDebug.debug("\tposition %f,%f\n",
+                                                        saved_state.state.gps.lat,
+                                                        saved_state.state.gps.lon);
+                               telemetry_state.states.put(serial, saved_state.state);
+                       }
+               }
 
                // Listen for GPS and Network position updates
                LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
 
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, this);
-
-               String address = AltosDroidPreferences.active_device();
-               if (address != null)
-                       startAltosBluetooth(address);
        }
 
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
-               Log.i("TelemetryService", "Received start id " + startId + ": " + intent);
+               AltosDebug.debug("Received start id %d: %s", startId, intent);
 
                CharSequence text = getText(R.string.telemetry_service_started);
 
@@ -354,6 +513,20 @@ public class TelemetryService extends Service implements LocationListener {
                // Move us into the foreground.
                startForeground(NOTIFICATION, notification);
 
+               /* Start bluetooth if we don't have a connection already */
+               if (intent != null &&
+                   (telemetry_state.connect == TelemetryState.CONNECT_NONE ||
+                    telemetry_state.connect == TelemetryState.CONNECT_DISCONNECTED))
+               {
+                       String  action = intent.getAction();
+
+                       if (action.equals(AltosDroid.ACTION_BLUETOOTH)) {
+                               DeviceAddress address = AltosDroidPreferences.active_device();
+                               if (address != null && !address.address.startsWith("USB"))
+                                       start_altos_bluetooth(address, false);
+                       }
+               }
+
                // We want this service to continue running until it is explicitly
                // stopped, so return sticky.
                return START_STICKY;
@@ -366,28 +539,25 @@ public class TelemetryService extends Service implements LocationListener {
                ((LocationManager) getSystemService(Context.LOCATION_SERVICE)).removeUpdates(this);
 
                // Stop the bluetooth Comms threads
-               stopAltosBluetooth();
+               disconnect(true);
 
                // 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();
+               return messenger.getBinder();
        }
 
 
        public void onLocationChanged(Location location) {
                telemetry_state.location = location;
-               if (D) Log.d(TAG, "location changed");
-               sendMessageToClients();
+               AltosDebug.debug("location changed");
+               send_to_clients();
        }
 
        public void onStatusChanged(String provider, int status, Bundle extras) {
index 862847d2b9af0982424e3ce20842fa47a466d842..f9191a32ebdcd9010d0eea39e354fdeadb7d56a8 100644 (file)
 
 package org.altusmetrum.AltosDroid;
 
-import org.altusmetrum.altoslib_6.*;
+import java.util.*;
+import org.altusmetrum.altoslib_8.*;
 import android.location.Location;
 
 public class TelemetryState {
-       public static final int CONNECT_NONE       = 0;
-       public static final int CONNECT_READY      = 1;
-       public static final int CONNECT_CONNECTING = 2;
-       public static final int CONNECT_CONNECTED  = 3;
+       public static final int CONNECT_NONE         = 0;
+       public static final int CONNECT_DISCONNECTED = 1;
+       public static final int CONNECT_CONNECTING   = 2;
+       public static final int CONNECT_CONNECTED    = 3;
 
        int             connect;
+       DeviceAddress   address;
        AltosConfigData config;
-       AltosState      state;
        Location        location;
        int             crc_errors;
+       double          receiver_battery;
        double          frequency;
        int             telemetry_rate;
 
+       HashMap<Integer,AltosState>     states;
+
+       int             latest_serial;
+
        public TelemetryState() {
                connect = CONNECT_NONE;
                config = null;
-               state = null;
+               states = new HashMap<Integer,AltosState>();
                location = null;
                crc_errors = 0;
+               receiver_battery = AltosLib.MISSING;
                frequency = AltosPreferences.frequency(0);
                telemetry_rate = AltosPreferences.telemetry_rate(0);
        }
index ff0fd7108e117edf0bde0b9c4de3b8072e05c2c7..dc8b7e5e617886436afba943705b915e49566d2e 100644 (file)
@@ -1,3 +1,4 @@
 bin
 classaltoslib.stamp
 altoslib*.jar
+AltosVersion.java
index b11dc3a119bebdf30423e061e3be625cae081c72..e4e7df903a27aa5cb8d89b249d0abc538d61f349 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index 4167aecfc201a0f706d52045c819cb7f88ec46d6..eaff0808fb57493c314a2461480c4ce618630f54 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosCRCException extends Exception {
        public int rssi;
index 02f7806f0274636b30290c3565521bf645aa4f69..edb23e69f93183eb80f31f7785cb31741d655ff0 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 8992b018775f2468913c91c604599fac5a8470d1..86b23eb60fd11bab25ce9995dba6d59b1958c37d 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index d8f4d945e466bf5d9d8e61f35b721bd414e6ae31..8e0529344d50cafaf5077e9e6bb3a62dd953b80d 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.*;
 import java.text.*;
@@ -75,6 +75,7 @@ public class AltosConfigData implements Iterable<String> {
        /* HAS_APRS */
        public int              aprs_interval;
        public int              aprs_ssid;
+       public int              aprs_format;
 
        /* HAS_BEEP */
        public int              beep;
@@ -270,6 +271,7 @@ public class AltosConfigData implements Iterable<String> {
 
                aprs_interval = -1;
                aprs_ssid = -1;
+               aprs_format = -1;
 
                beep = -1;
 
@@ -370,6 +372,7 @@ public class AltosConfigData implements Iterable<String> {
                /* HAS_APRS */
                try { aprs_interval = get_int(line, "APRS interval:"); } catch (Exception e) {}
                try { aprs_ssid = get_int(line, "APRS SSID:"); } catch (Exception e) {}
+               try { aprs_format = get_int(line, "APRS format:"); } catch (Exception e) {}
 
                /* HAS_BEEP */
                try { beep = get_int(line, "Beeper setting:"); } catch (Exception e) {}
@@ -518,6 +521,8 @@ public class AltosConfigData implements Iterable<String> {
                        aprs_interval = source.aprs_interval();
                if (aprs_ssid >= 0)
                        aprs_ssid = source.aprs_ssid();
+               if (aprs_format >= 0)
+                       aprs_format = source.aprs_format();
 
                /* HAS_BEEP */
                if (beep >= 0)
@@ -572,6 +577,7 @@ public class AltosConfigData implements Iterable<String> {
                dest.set_pyro_firing_time(pyro_firing_time);
                dest.set_aprs_interval(aprs_interval);
                dest.set_aprs_ssid(aprs_ssid);
+               dest.set_aprs_format(aprs_format);
                dest.set_beep(beep);
                dest.set_tracker_motion(tracker_motion);
                dest.set_tracker_interval(tracker_interval);
@@ -674,6 +680,8 @@ public class AltosConfigData implements Iterable<String> {
                        link.printf("c A %d\n", aprs_interval);
                if (aprs_ssid >= 0)
                        link.printf("c S %d\n", aprs_ssid);
+               if (aprs_format >= 0)
+                       link.printf("c C %d\n", aprs_format);
 
                /* HAS_BEEP */
                if (beep >= 0)
index 11aa4d24bcebdda00b17c3b8566749397eb86264..da11336d8bb050cb4e188c7e1e11b68e47d69b14 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosConfigDataException extends Exception {
 
index cfe9fc8b960302b3a0fe443c25e6d4bf3b07e3bc..f8a2fb14353fbe1ff08f819b4146916623b68224 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public interface AltosConfigValues {
        /* set and get all of the dialog values */
@@ -91,6 +91,10 @@ public interface AltosConfigValues {
 
        public abstract void set_aprs_ssid(int new_aprs_ssid);
 
+       public abstract int aprs_format() throws AltosConfigDataException;
+
+       public abstract void set_aprs_format(int new_aprs_format);
+
        public abstract int beep() throws AltosConfigDataException;
 
        public abstract void set_beep(int new_beep);
index a5eb7ff8258c578b5ffc2f28a1d2a94c72bb1d5e..fd2f5750ef6ed4aafd79970438408cff6ddfc760 100644 (file)
@@ -18,7 +18,7 @@
 /*
  * Sensor data conversion functions
  */
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosConvert {
        /*
@@ -230,6 +230,12 @@ public class AltosConvert {
                return sensor / 32767.0 * supply * (5.6 + 10.0) / 10.0;
        }
 
+       static double tele_bt_3_battery(int raw) {
+               if (raw == AltosLib.MISSING)
+                       return AltosLib.MISSING;
+               return 3.3 * mega_adc(raw) * (5.1 + 10.0) / 10.0;
+       }
+
        static double easy_mini_voltage(int sensor, int serial) {
                double  supply = 3.3;
                double  diode_offset = 0.0;
index ef5edc6ac6b364924f12f81538aeacf13086e40f..16dcf6f5fac321c33ea4183b5ad23f179524920f 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index b0666f339317e04d46fd1430863a6ba2277b4db8..a05fb7e2e5380ca6b37a2d877ef1bfadec1d8bf4 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosDistance extends AltosUnits {
 
index 777988e74a554237b60ee79ccbafb9a1a0f6a62e..194b10e801d3f9c96c7ef168ed7dfaad3c46e791 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 19a8807df22b31bc3e490d6b8e3dd7bbfe29c028..9174c6a9c75193b1b15b03e751804f9b888b6078 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.text.*;
 import java.util.concurrent.*;
index 9598bd93e88456cc5f946ad6a3496f2a0103f424..baaeb9933c05b9a75bb2d6cc073dd05b1cde6ec5 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index c8443549d10a9c267a04821787800238dfc31b42..33d4f63be4521042d06de1eb9dc02b19ccbfed40 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 96cfc0e1fbc5c3b282578089fae0539d433d66f5..aeb61661b9c9d7e0ca60d92c1a8c5ff482b6fc3f 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index fc4e9caadf79b56f3610988c6ca820cc60f0638c..95ecc32c73264a9f5924e3fe8cf5857ef3ae63f7 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 94487ab52ee04f53fb982ae0e5cce8907fcbae85..c3cd5d86c87903c00ea9737b5dfd0765eb57576c 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 39768a21b6dc87cde3985e671f5b27f55d0b6e50..6dc6a3d655f6b1d7014050c4dfd050c53873736c 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 0fc82ab453732d61b4d9f3c40a20872b1694279e..538c102bdea1ca0d96c0f4264a60aac23cfd7935 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.text.*;
 import java.util.concurrent.*;
index c2edcf233926d842824cb7f8c6b8c28df0a2aa01..d5f0e0e0f0e422fdf84bc0b28fa6e23bbee72aa2 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 39425e100c0aa5ac82d6f353dd2dc5286fa49544..1b4cb04a3dfff898ed17fc99b52b21af36538d08 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index ef13d0a72ffcbc45d084f67eb3d49399ac039c3c..8d0343b80738f442565a7f04ee0eada667291606 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 35ed5a6e0fb8107c06aad08cd04dab568f385611..1c1d1010dd6bc10d8d548ef028f3ad5a4dbc992c 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public interface AltosEepromMonitor {
 
index 2bdd64f0da171aed6e58b038367f8f9217ac648b..44b98e49b8959d1eec14ca7e7a20cf0d7c2b4d42 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 36500393ebd1e076c11a83f2d3c4e9f837747c4c..0a37a2aa68a78a45fdbcc054a0f7483415937b37 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index a79216a2682d3eddf518c78882d63cd2ce1d983d..cab38d6dd20d8d020439e28ec2f52f9f2ab5cce3 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.File;
 import java.util.*;
index e58b652e702477ebc12f3701c9f0cffe5b260e14..e4b980b07cc1dcbdc97a906ec639e80fb13a5b77 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index 1c7cd77db0d6fec54b77a4efbea4224d191a1056..6a7351f367a2c32efbd257f91a836017e1041167 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public interface AltosFlashListener {
        public void position(String label, int percent);
diff --git a/altoslib/AltosFlightDisplay.java b/altoslib/AltosFlightDisplay.java
new file mode 100644 (file)
index 0000000..33d71fc
--- /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_8;
+
+public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
+       void reset();
+
+       void show(AltosState state, AltosListenerState listener_state);
+
+       String getName();
+}
index 03c53ff4d8f7c580ecfdf126723048663b8d29ab..fca0f20b04fe5cce36e93ad086ff04747ac94e5d 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.text.*;
 import java.io.*;
index 82e477f8e2fd1d2b6d3d63889e30665c7d4eb2d8..b837ba841bca3f2035dbb2362ae9117f949872cc 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
diff --git a/altoslib/AltosFontListener.java b/altoslib/AltosFontListener.java
new file mode 100644 (file)
index 0000000..7804935
--- /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 org.altusmetrum.altoslib_8;
+
+public interface AltosFontListener {
+       void font_size_changed(int font_size);
+}
index 1dd4819dbff1335c4f6a98c6f4ebaa89566ecd5c..6d2bb8d0a5836dd0e3424ba8643f4eece6eccb4d 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosFrequency {
        public double   frequency;
        public String   description;
 
+       public int hashCode() {
+               return new Double(frequency).hashCode();
+       }
+
+       public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+               if (!(o instanceof AltosFrequency))
+                       return false;
+               AltosFrequency other = (AltosFrequency) o;
+               return other.frequency == frequency;
+       }
+
        public String toString() {
                return String.format("%7.3f MHz %-20s",
                                     frequency, description);
index 2139efb22fe3e6c106c963a43dab2a1b308dba5f..a2584e7789330703ea8e8562aa9bb6450b94831a 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.text.*;
 import java.util.concurrent.*;
@@ -195,10 +195,10 @@ public class AltosGPS implements Cloneable, Serializable {
                        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)"));
+                               ground_speed = AltosParse.parse_double_net(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)"));
+                               climb_rate = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "m/s(V)"));
+                               hdop = AltosParse.parse_double_net(AltosParse.strip_suffix(words[i++], "(hdop)"));
                                h_error = AltosParse.parse_int(words[i++]);
                                v_error = AltosParse.parse_int(words[i++]);
                        }
index 57491f4d91553cda5feba62a4ce36e118bbc2f22..44782003a68aab9ffae87c62aa4a472584f583d1 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosGPSSat {
        public int      svid;
index c48755be4f73e11d8d3c8f301b159ac0fafe90c3..e13eca487cbd93ac1e53efc8492dd0256a8d89fa 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.lang.Math;
 import java.io.*;
@@ -28,8 +28,8 @@ public class AltosGreatCircle implements Cloneable, Serializable {
 
        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 double rad = Math.PI / 180;
+       public static final double earth_radius = 6371.2 * 1000;        /* in meters */
 
        public static final int BEARING_LONG = AltosConvert.BEARING_LONG;
        public static final int BEARING_SHORT = AltosConvert.BEARING_SHORT;
index c4419ae6d385c994deaaf2b65b1062c7a0d75cd0..bc20ef24a20ae13d3481d0cbd24d75524ce18b41 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosHeight extends AltosUnits {
 
index 9f45d65ac52bc8dac7fd62e7dbf020bc8d85f553..ece822d83d6608f40aaadfb3cf14c4ca07f86380 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.LinkedList;
index 0c61fd02461740c337a86570d39252310ff50d2c..e1561c7dcd64f58ac2d1a031c02e5f3ecc3f7caf 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosHexsym {
        String  name;
index 8c219d9f163464eec3d796ce28adfeb50e816ef7..7a4a705d9df238271b34614fd190b9dbcdc6ff87 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.*;
 import java.io.*;
index 82b18ca23934d583b9d43a660a808632bbb79e22..0e2576cda3df01666f36660cf46a50c0fb2d95da 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 4c5e8285a7d3c7459b85a9484f55dc17ee978f11..6c0d130ba6571a876eec58cf95413f5572528ddb 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
@@ -146,6 +146,7 @@ public class AltosIdleFetch implements AltosStateUpdate {
                        state.set_callsign(config_data.callsign);
                        state.set_ground_accel(config_data.accel_cal_plus);
                        state.set_accel_g(config_data.accel_cal_plus, config_data.accel_cal_minus);
+                       state.set_product(config_data.product);
                        for (AltosIdler idler : idlers) {
                                if (idler.matches(config_data)) {
                                        idler.update_state(state, link, config_data);
index 4d0968bf3d26c3e36f95173c4efde83afcbc1e84..1b62f385fac0714b2667cde1a8bd032e721e0c40 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.concurrent.*;
index fdf4be9d6469813d7ce9380dd77d9971e41de9b4..1d06c4d9e2015c2dffefe110e773f2d3d26da776 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public interface AltosIdleMonitorListener {
        public void update(AltosState state, AltosListenerState listener_state);
index ffb6ed158d1e74d6bef8afaef9fa1ac80ade351b..39f792db582ffe206dbe07a6cedb28f64f090069 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.*;
 import java.io.*;
diff --git a/altoslib/AltosImage.java b/altoslib/AltosImage.java
new file mode 100644 (file)
index 0000000..fc0192a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+
+public interface AltosImage {
+       /* Discard storage for image */
+       public abstract void flush();
+}
index aa80fc21dd8465ee5ac17769c5179343ef440cdf..814339580cca39a4975cbaba3c4d46393da8e58e 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
+import java.util.*;
+
+class KMLWriter extends PrintWriter {
+       public PrintWriter printf(String format, Object ... arguments) {
+               return printf(Locale.ROOT, format, arguments);
+       }
+
+       public KMLWriter(File name) throws FileNotFoundException {
+               super(name);
+       }
+}
 
 public class AltosKML implements AltosWriter {
 
        File                    name;
-       PrintStream             out;
+       PrintWriter             out;
        int                     flight_state = -1;
        AltosState              prev = null;
        double                  gps_start_altitude;
@@ -137,6 +148,10 @@ public class AltosKML implements AltosWriter {
                        end();
                        prev = null;
                }
+               if (out != null) {
+                       out.close();
+                       out = null;
+               }
        }
 
        public void write(AltosState state) {
@@ -177,7 +192,7 @@ public class AltosKML implements AltosWriter {
 
        public AltosKML(File in_name) throws FileNotFoundException {
                name = in_name;
-               out = new PrintStream(name);
+               out = new KMLWriter(name);
        }
 
        public AltosKML(String in_string) throws FileNotFoundException {
diff --git a/altoslib/AltosLatLon.java b/altoslib/AltosLatLon.java
new file mode 100644 (file)
index 0000000..e438a78
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public class AltosLatLon {
+       public double   lat;
+       public double   lon;
+
+       public int hashCode() {
+               return new Double(lat).hashCode() ^ new Double(lon).hashCode();
+       }
+
+       public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+               if (!(o instanceof AltosLatLon))
+                       return false;
+
+               AltosLatLon other = (AltosLatLon) o;
+               return lat == other.lat && lon == other.lon;
+       }
+
+       public String toString() {
+               return String.format("%f/%f", lat, lon);
+       }
+
+       public AltosLatLon(double lat, double lon) {
+               this.lat = lat;
+               this.lon = lon;
+       }
+}
index 191a46eeb011a40e93692226664accba8aecf243..7091ce9ce2bdbf655836772ed41deeea646bc08a 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosLatitude extends AltosLocation {
        public String pos() { return "N"; }
diff --git a/altoslib/AltosLaunchSite.java b/altoslib/AltosLaunchSite.java
new file mode 100644 (file)
index 0000000..0fa9bbd
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.text.*;
+import java.util.concurrent.*;
+
+public class AltosLaunchSite {
+       public String   name;
+       public double   latitude;
+       public double   longitude;
+
+       public String toString() {
+               return name;
+       }
+
+       public AltosLaunchSite(String in_name, double in_latitude, double in_longitude) {
+               name = in_name;
+               latitude = in_latitude;
+               longitude = in_longitude;
+       }
+
+       public AltosLaunchSite(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 = AltosParse.parse_double_net(elements[1]);
+                       longitude = AltosParse.parse_double_net(elements[2]);
+               } catch (ParseException pe) {
+                       throw new ParseException(String.format("Invalid site line %s", line), 0);
+               }
+       }
+}
+
diff --git a/altoslib/AltosLaunchSiteListener.java b/altoslib/AltosLaunchSiteListener.java
new file mode 100644 (file)
index 0000000..f465866
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+public interface AltosLaunchSiteListener {
+       public abstract void notify_launch_sites(List<AltosLaunchSite> sites);
+}
diff --git a/altoslib/AltosLaunchSites.java b/altoslib/AltosLaunchSites.java
new file mode 100644 (file)
index 0000000..fa4026b
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.net.*;
+import java.text.*;
+
+public class AltosLaunchSites extends Thread {
+       URL                             url;
+       LinkedList<AltosLaunchSite>     sites;
+       AltosLaunchSiteListener         listener;
+
+       void notify_complete() {
+               listener.notify_launch_sites(sites);
+       }
+
+       void add(AltosLaunchSite site) {
+               sites.add(site);
+       }
+
+       void add(String line) {
+               try {
+                       add(new AltosLaunchSite(line));
+               } catch (ParseException pe) {
+                       System.out.printf("parse exception %s\n", pe.toString());
+               }
+       }
+
+       public void run() {
+               try {
+                       url = new URL(AltosLib.launch_sites_url);
+                       URLConnection uc = url.openConnection();
+
+                       InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.unicode_set);
+                       BufferedReader in = new BufferedReader(in_stream);
+
+                       for (;;) {
+                               String line = in.readLine();
+                               if (line == null)
+                                       break;
+                               add(line);
+                       }
+               } catch (Exception e) {
+               } finally {
+                       notify_complete();
+               }
+       }
+
+       public AltosLaunchSites(AltosLaunchSiteListener listener) {
+               sites = new LinkedList<AltosLaunchSite>();
+               this.listener = listener;
+               start();
+       }
+}
index b19f9f524c8385206d87edc2b6a77bdb22ad6054..e82b1c73bf990f52883fe98cdbb6a48a78b89b12 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.*;
 import java.io.*;
@@ -190,6 +190,13 @@ public class AltosLib {
                38400, 9600, 2400
        };
 
+       public static final int ao_aprs_format_compressed = 0;
+       public static final int ao_aprs_format_uncompressed = 1;
+
+       public static final String[] ao_aprs_format_name = {
+               "Compressed", "Uncompressed"
+       };
+
        public static final String launch_sites_url = "http://www.altusmetrum.org/AltOS/launch-sites.txt";
 //     public static final String launch_sites_url = "file:///home/keithp/misc/text/altusmetrum/AltOS/launch-sites.txt";
 
index a65ba59314237f10f86d1a71e2615ed7d59d8821..b18aa9650b4c43412708e98ca26db1b5262d5c2a 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosLine {
        public String   line;
index 01c378645d23c52fe3bee3416be39b605de05383..f8bf4702cca713efdb2096eb4759f0c87c7861df 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.concurrent.*;
@@ -86,10 +86,17 @@ public abstract class AltosLink implements Runnable {
 
        public boolean  reply_abort;
        public int      in_reply;
+       boolean cancel_enable = true;
+
+       public void set_cancel_enable(boolean e) {
+               cancel_enable = e;
+       }
 
        boolean         reply_timeout_shown = false;
 
        private boolean check_reply_timeout() {
+               if (!cancel_enable)
+                       return false;
                if (!reply_timeout_shown)
                        reply_timeout_shown = show_reply_timeout();
                return reply_abort;
@@ -354,7 +361,7 @@ public abstract class AltosLink implements Runnable {
                if (frequency == 0)
                        return;
                if (has_frequency)
-                       set_radio_freq((int) Math.floor (frequency * 1000));
+                       set_radio_freq((int) Math.floor (frequency * 1000 + 0.5));
                else if (has_setting)
                        set_radio_setting(AltosConvert.radio_frequency_to_setting(frequency, cal));
                else
@@ -546,7 +553,15 @@ public abstract class AltosLink implements Runnable {
                }
                if (monitor_batt == AltosLib.MISSING)
                        return AltosLib.MISSING;
-               return AltosConvert.cc_battery_to_voltage(monitor_batt);
+
+               double  volts = AltosLib.MISSING;
+               if (config_data.product.startsWith("TeleBT-v3")) {
+                       volts = AltosConvert.tele_bt_3_battery(monitor_batt);
+               } else {
+                       volts = AltosConvert.cc_battery_to_voltage(monitor_batt);
+               }
+
+               return volts;
        }
 
        public AltosLink() {
index d7e180080aaf9c67f0a68e48bd3503ea522ec592..9d7922d59c206ae1dd5c126f59cd90ebd6fe2c85 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index b21014db0a416a4398fb2a81e49c38d8a83829b3..231cd94b3488a2b5edab0daa52457b94b95ad3ab 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public abstract class AltosLocation extends AltosUnits {
 
index 9241581a2049e0f8c303307173c5a611cd5799b5..b89bac38536f0359379d1c1f2b506581df694621 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.text.*;
index ff4f0c2b01ea5419c94597c01a967591639a4b7f..e3e532826604690f9cec9bfd311bbf985d56d250 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosLongitude extends AltosLocation {
        public String pos() { return "E"; }
index f7595639378c489726170b5d4dd23f76f243862e..351125bf407d6919d116e30d5e3d2a4e7159ce24 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.*;
 import java.io.*;
diff --git a/altoslib/AltosMap.java b/altoslib/AltosMap.java
new file mode 100644 (file)
index 0000000..8a3266c
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * 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 org.altusmetrum.altoslib_8;
+
+import java.io.*;
+import java.lang.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMap implements AltosMapTileListener, AltosMapStoreListener {
+
+       public static final int px_size = 512;
+
+       public static final int maptype_hybrid = 0;
+       public static final int maptype_roadmap = 1;
+       public static final int maptype_satellite = 2;
+       public static final int maptype_terrain = 3;
+       public static final int maptype_default = maptype_hybrid;
+
+       public static final int default_zoom = 15;
+       public static final int min_zoom = 3;
+       public static final int max_zoom = 21;
+
+       public static final String[] maptype_names = {
+               "hybrid",
+               "roadmap",
+               "satellite",
+               "terrain"
+       };
+
+       public static final String[] maptype_labels = {
+               "Hybrid",
+               "Roadmap",
+               "Satellite",
+               "Terrain"
+       };
+
+       AltosMapInterface       map_interface;
+
+       AltosMapCache           cache;
+
+       public AltosMapCache cache() { return cache; }
+
+       LinkedList<AltosMapMark> marks = new LinkedList<AltosMapMark>();
+
+       AltosMapPath            path;
+       AltosMapLine            line;
+       public AltosLatLon      last_position;
+
+       boolean         have_boost = false;
+       boolean         have_landed = false;
+
+       ConcurrentHashMap<AltosPointInt,AltosMapTile> tiles = new ConcurrentHashMap<AltosPointInt,AltosMapTile>();
+       int             load_radius;
+       AltosLatLon     load_centre = null;
+       AltosMapTileListener    load_listener;
+
+       int             zoom = AltosMap.default_zoom;
+       int             maptype = AltosMap.maptype_default;
+
+       long            user_input_time;
+
+       /* Milliseconds to wait after user action before auto-scrolling
+        */
+       static final long auto_scroll_delay = 20 * 1000;
+
+       public AltosMapTransform        transform;
+       AltosLatLon             centre;
+
+       public void reset() {
+               // nothing
+       }
+
+       /* MapInterface wrapping functions */
+
+       public void repaint(int x, int y, int w, int h) {
+               map_interface.repaint(new AltosRectangle(x, y, w, h));
+       }
+
+       public void repaint(AltosMapRectangle damage, int pad) {
+               AltosRectangle r = transform.screen(damage);
+               repaint(r.x - pad, r.y - pad, r.width + pad * 2, r.height + pad * 2);
+       }
+
+       public void repaint() {
+               map_interface.repaint();
+       }
+
+       public int width() {
+               return map_interface.width();
+       }
+
+       public int height() {
+               return map_interface.height();
+       }
+
+       public void debug(String format, Object ... arguments) {
+               map_interface.debug(format, arguments);
+       }
+
+       static public AltosPointInt floor(AltosPointDouble point) {
+               return new AltosPointInt ((int) Math.floor(point.x / AltosMap.px_size) * AltosMap.px_size,
+                                             (int) Math.floor(point.y / AltosMap.px_size) * AltosMap.px_size);
+       }
+
+       static public AltosPointInt ceil(AltosPointDouble point) {
+               return new AltosPointInt ((int) Math.ceil(point.x / AltosMap.px_size) * AltosMap.px_size,
+                                             (int) Math.ceil(point.y / AltosMap.px_size) * AltosMap.px_size);
+       }
+
+       public void notice_user_input() {
+               user_input_time = System.currentTimeMillis();
+       }
+
+       public boolean recent_user_input() {
+               return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay;
+       }
+
+       public boolean has_centre() {
+               return centre != null;
+       }
+
+       public boolean far_from_centre(AltosLatLon lat_lon) {
+
+               if (centre == null || transform == null)
+                       return true;
+
+               AltosPointDouble        screen = transform.screen(lat_lon);
+
+               int             width = width();
+               int             dx = Math.abs ((int) (double) screen.x - width/2);
+
+               if (dx > width / 4)
+                       return true;
+
+               int             height = height();
+               int             dy = Math.abs ((int) (double) screen.y - height/2);
+
+               if (dy > height / 4)
+                       return true;
+
+               return false;
+       }
+
+       public void set_transform() {
+               if (centre != null) {
+                       transform = new AltosMapTransform(width(), height(), zoom, centre);
+                       repaint();
+               }
+       }
+
+       private void set_zoom_label() {
+               map_interface.set_zoom_label(String.format("Zoom %d", get_zoom() - default_zoom));
+       }
+
+
+       public boolean set_zoom(int zoom) {
+               notice_user_input();
+               if (AltosMap.min_zoom <= zoom && zoom <= AltosMap.max_zoom && zoom != this.zoom) {
+                       this.zoom = zoom;
+                       tiles.clear();
+                       set_transform();
+                       set_zoom_label();
+                       return true;
+               }
+               return false;
+       }
+
+       public boolean set_zoom_centre(int zoom, AltosPointInt centre) {
+               AltosLatLon     mouse_lat_lon = null;
+               boolean         ret;
+
+               if (transform != null)
+                       mouse_lat_lon = transform.screen_lat_lon(centre);
+
+               ret = set_zoom(zoom);
+
+               if (ret && mouse_lat_lon != null) {
+                       AltosPointDouble        new_mouse = transform.screen(mouse_lat_lon);
+
+                       double  dx = width()/2.0 - centre.x;
+                       double  dy = height()/2.0 - centre.y;
+
+                       AltosLatLon     new_centre = transform.screen_lat_lon(new AltosPointDouble(new_mouse.x + dx, new_mouse.y + dy));
+
+                       centre(new_centre);
+               }
+
+               return ret;
+       }
+
+       public int get_zoom() {
+               return zoom;
+       }
+
+       public boolean set_maptype(int maptype) {
+               if (maptype != this.maptype) {
+                       this.maptype = maptype;
+                       tiles.clear();
+                       repaint();
+                       return true;
+               }
+               return false;
+       }
+
+       public void show(AltosState state, AltosListenerState listener_state) {
+
+               /* If insufficient gps data, nothing to update
+                */
+               AltosGPS        gps = state.gps;
+
+               if (gps == null)
+                       return;
+
+               if (!gps.locked && gps.nsat < 4)
+                       return;
+
+               switch (state.state) {
+               case AltosLib.ao_flight_boost:
+                       if (!have_boost) {
+                               add_mark(gps.lat, gps.lon, state.state);
+                               have_boost = true;
+                       }
+                       break;
+               case AltosLib.ao_flight_landed:
+                       if (!have_landed) {
+                               add_mark(gps.lat, gps.lon, state.state);
+                               have_landed = true;
+                       }
+                       break;
+               }
+
+               if (path != null) {
+                       AltosMapRectangle       damage = path.add(gps.lat, gps.lon, state.state);
+
+                       if (damage != null)
+                               repaint(damage, AltosMapPath.stroke_width);
+               }
+
+               last_position = new AltosLatLon(gps.lat, gps.lon);
+
+               maybe_centre(gps.lat, gps.lon);
+       }
+
+       public void centre(AltosLatLon lat_lon) {
+               centre = lat_lon;
+               set_transform();
+       }
+
+       public void centre(double lat, double lon) {
+               centre(new AltosLatLon(lat, lon));
+       }
+
+       public void centre(AltosState state) {
+               if (!state.gps.locked && state.gps.nsat < 4)
+                       return;
+               centre(state.gps.lat, state.gps.lon);
+       }
+
+       public void maybe_centre(double lat, double lon) {
+               AltosLatLon     lat_lon = new AltosLatLon(lat, lon);
+               if (centre == null || (!recent_user_input() && far_from_centre(lat_lon)))
+                       centre(lat_lon);
+       }
+
+       public void add_mark(double lat, double lon, int state) {
+               synchronized(marks) {
+                       AltosMapMark mark = map_interface.new_mark(lat, lon, state);
+                       if (mark != null)
+                               marks.add(mark);
+               }
+               repaint();
+       }
+
+       public void clear_marks() {
+               synchronized(marks) {
+                       marks.clear();
+               }
+       }
+
+       private void make_tiles() {
+               AltosPointInt   upper_left;
+               AltosPointInt   lower_right;
+
+               if (load_centre != null) {
+                       AltosPointInt centre = floor(transform.point(load_centre));
+
+                       upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
+                                                              centre.y - load_radius * AltosMap.px_size);
+                       lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
+                                                               centre.y + load_radius * AltosMap.px_size);
+               } else {
+                       upper_left = floor(transform.screen_point(new AltosPointInt(0, 0)));
+                       lower_right = floor(transform.screen_point(new AltosPointInt(width(), height())));
+               }
+               for (AltosPointInt point : tiles.keySet()) {
+                       if (point.x < upper_left.x || lower_right.x < point.x ||
+                           point.y < upper_left.y || lower_right.y < point.y) {
+                               tiles.remove(point);
+                       }
+               }
+
+               cache.set_cache_size((width() / AltosMap.px_size + 2) * (height() / AltosMap.px_size + 2));
+
+               for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
+                       for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
+                               AltosPointInt   point = new AltosPointInt(x, y);
+
+                               if (!tiles.containsKey(point)) {
+                                       AltosLatLon     ul = transform.lat_lon(point);
+                                       AltosLatLon     center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
+                                       AltosMapTile tile = map_interface.new_tile(this, ul, center, zoom, maptype, px_size);
+                                       tiles.put(point, tile);
+                               }
+                       }
+               }
+       }
+
+       public void set_load_params(int new_zoom, int new_type, double lat, double lon, int radius, AltosMapTileListener listener) {
+               if (AltosMap.min_zoom <= new_zoom && new_zoom <= AltosMap.max_zoom)
+                       zoom = new_zoom;
+               maptype = new_type;
+               load_centre = new AltosLatLon(lat, lon);
+               load_radius = radius;
+               load_listener = listener;
+               centre(lat, lon);
+               tiles.clear();
+               make_tiles();
+               for (AltosMapTile tile : tiles.values()) {
+                       tile.add_store_listener(this);
+                       if (tile.store_status() != AltosMapTile.loading)
+                               listener.notify_tile(tile, tile.store_status());
+               }
+               repaint();
+       }
+
+       public String getName() {
+               return "Map";
+       }
+
+       public void paint() {
+               if (centre != null)
+                       make_tiles();
+
+               if (transform == null)
+                       return;
+
+               for (AltosMapTile tile : tiles.values())
+                       tile.paint(transform);
+
+               synchronized(marks) {
+                       for (AltosMapMark mark : marks)
+                               mark.paint(transform);
+               }
+
+               if (path != null)
+                       path.paint(transform);
+
+               if (line != null)
+                       line.paint(transform);
+       }
+
+       /* AltosMapTileListener methods */
+       public synchronized void notify_tile(AltosMapTile tile, int status) {
+               for (AltosPointInt point : tiles.keySet()) {
+                       if (tile == tiles.get(point)) {
+                               AltosPointInt   screen = transform.screen(point);
+                               repaint(screen.x, screen.y, AltosMap.px_size, AltosMap.px_size);
+                       }
+               }
+       }
+
+       /* AltosMapStoreListener methods */
+       public synchronized void notify_store(AltosMapStore store, int status) {
+               if (load_listener != null) {
+                       for (AltosMapTile tile : tiles.values())
+                               if (store.equals(tile.store))
+                                       load_listener.notify_tile(tile, status);
+               }
+       }
+
+       /* UI elements */
+
+       AltosPointInt   drag_start;
+
+       boolean         dragged;
+
+       static final double drag_far = 20;
+
+       private void drag(int x, int y) {
+               if (drag_start == null)
+                       return;
+
+               int dx = x - drag_start.x;
+               int dy = y - drag_start.y;
+
+               double distance = Math.hypot(dx, dy);
+
+               if (distance > drag_far)
+                       dragged = true;
+
+               if (transform == null) {
+                       debug("Transform not set in drag\n");
+                       return;
+               }
+
+               AltosLatLon new_centre = transform.screen_lat_lon(new AltosPointInt(width() / 2 - dx, height() / 2 - dy));
+               centre(new_centre);
+               drag_start = new AltosPointInt(x, y);
+       }
+
+       private void drag_start(int x, int y) {
+               drag_start = new AltosPointInt(x, y);
+               dragged = false;
+       }
+
+       private void drag_stop(int x, int y) {
+               if (!dragged) {
+                       if (transform == null) {
+                               debug("Transform not set in stop\n");
+                               return;
+                       }
+                       map_interface.select_object (transform.screen_lat_lon(new AltosPointInt(x,y)));
+               }
+       }
+
+       private void line_start(int x, int y) {
+               if (line != null) {
+                       line.pressed(new AltosPointInt(x, y), transform);
+                       repaint();
+               }
+       }
+
+       private void line(int x, int y) {
+               if (line != null) {
+                       line.dragged(new AltosPointInt(x, y), transform);
+                       repaint();
+               }
+       }
+
+       public void touch_start(int x, int y, boolean is_drag) {
+               notice_user_input();
+               if (is_drag)
+                       drag_start(x, y);
+               else
+                       line_start(x, y);
+       }
+
+       public void touch_continue(int x, int y, boolean is_drag) {
+               notice_user_input();
+               if (is_drag)
+                       drag(x, y);
+               else
+                       line(x, y);
+       }
+
+       public void touch_stop(int x, int y, boolean is_drag) {
+               notice_user_input();
+               if (is_drag)
+                       drag_stop(x, y);
+       }
+
+       public AltosMap(AltosMapInterface map_interface) {
+               this.map_interface = map_interface;
+               cache = new AltosMapCache(map_interface);
+               line = map_interface.new_line();
+               path = map_interface.new_path();
+               set_zoom_label();
+       }
+}
diff --git a/altoslib/AltosMapCache.java b/altoslib/AltosMapCache.java
new file mode 100644 (file)
index 0000000..f0ad433
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * 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 org.altusmetrum.altoslib_8;
+
+import java.io.*;
+import java.net.*;
+
+public class AltosMapCache implements AltosMapCacheListener {
+
+       /* An entry in the MapCache */
+       class MapCacheElement implements AltosMapStoreListener {
+
+               AltosMapTile            tile;           /* Notify when image has been loaded */
+               AltosImage              image;
+               AltosMapStore           store;
+               long                    used;
+
+               class loader implements Runnable {
+                       public void run() {
+                               if (image != null)
+                                       tile.notify_image(image);
+                               try {
+                                       image = map_interface.load_image(store.file);
+                               } catch (Exception ex) {
+                               }
+                               if (image == null)
+                                       tile.set_status(AltosMapTile.failed);
+                               else
+                                       tile.set_status(AltosMapTile.success);
+                               tile.notify_image(image);
+                       }
+               }
+
+               private void load() {
+                       loader  l = new loader();
+                       Thread  lt = new Thread(l);
+                       lt.start();
+               }
+
+               public void flush() {
+                       if (image != null) {
+                               image.flush();
+                               image = null;
+                       }
+               }
+
+               public boolean has_map() {
+                       return store.status() == AltosMapTile.success;
+               }
+
+               public synchronized void notify_store(AltosMapStore store, int status) {
+                       switch (status) {
+                       case AltosMapTile.loading:
+                               break;
+                       case AltosMapTile.success:
+                               load();
+                               break;
+                       default:
+                               tile.set_status(status);
+                               tile.notify_image(null);
+                       }
+               }
+
+               public MapCacheElement(AltosMapTile tile, AltosMapStore store) throws IOException {
+                       this.tile = tile;
+                       this.image = null;
+                       this.store = store;
+                       this.used = 0;
+
+                       int status = store.status();
+                       switch (status) {
+                       case AltosMapTile.loading:
+                               store.add_listener(this);
+                               break;
+                       case AltosMapTile.success:
+                               load();
+                               break;
+                       default:
+                               tile.set_status(status);
+                               tile.notify_image(null);
+                               break;
+                       }
+               }
+       }
+
+       int                     min_cache_size;         /* configured minimum cache size */
+       int                     cache_size;             /* current cache size */
+       int                     requested_cache_size;   /* cache size computed by application */
+
+       private Object          fetch_lock = new Object();
+       private Object          cache_lock = new Object();
+
+       AltosMapInterface       map_interface;
+
+       MapCacheElement[]       elements = new MapCacheElement[cache_size];
+
+       long                    used;
+
+       public void set_cache_size(int new_size) {
+
+               requested_cache_size = new_size;
+
+               if (new_size < min_cache_size)
+                       new_size = min_cache_size;
+
+               if (new_size == cache_size)
+                       return;
+
+               synchronized(cache_lock) {
+                       MapCacheElement[]       new_elements = new MapCacheElement[new_size];
+
+                       for (int i = 0; i < cache_size; i++) {
+                               if (i < new_size)
+                                       new_elements[i] = elements[i];
+                               else if (elements[i] != null)
+                                       elements[i].flush();
+                       }
+                       elements = new_elements;
+                       cache_size = new_size;
+               }
+       }
+
+       public AltosImage get(AltosMapTile tile, AltosMapStore store, int width, int height) {
+               int             oldest = -1;
+               long            age = used;
+
+               synchronized(cache_lock) {
+                       MapCacheElement element = null;
+                       for (int i = 0; i < cache_size; i++) {
+                               element = elements[i];
+
+                               if (element == null) {
+                                       oldest = i;
+                                       break;
+                               }
+                               if (store.equals(element.store)) {
+                                       element.used = used++;
+                                       return element.image;
+                               }
+                               if (element.used < age) {
+                                       oldest = i;
+                                       age = element.used;
+                               }
+                       }
+
+                       try {
+                               element = new MapCacheElement(tile, store);
+                               element.used = used++;
+                               if (elements[oldest] != null)
+                                       elements[oldest].flush();
+
+                               elements[oldest] = element;
+
+                               if (element.image == null)
+                                       tile.set_status(AltosMapTile.loading);
+                               else
+                                       tile.set_status(AltosMapTile.success);
+
+                               return element.image;
+                       } catch (IOException e) {
+                               tile.set_status(AltosMapTile.failed);
+                               return null;
+                       }
+               }
+       }
+
+       public void map_cache_changed(int map_cache) {
+               min_cache_size = map_cache;
+
+               set_cache_size(requested_cache_size);
+       }
+
+       public void dispose() {
+               AltosPreferences.unregister_map_cache_listener(this);
+
+               for (int i = 0; i < cache_size; i++) {
+                       MapCacheElement element = elements[i];
+
+                       if (element != null)
+                           element.flush();
+               }
+       }
+
+       public AltosMapCache(AltosMapInterface map_interface) {
+               this.map_interface = map_interface;
+               min_cache_size = AltosPreferences.map_cache();
+
+               set_cache_size(0);
+
+               AltosPreferences.register_map_cache_listener(this);
+       }
+}
diff --git a/altoslib/AltosMapCacheListener.java b/altoslib/AltosMapCacheListener.java
new file mode 100644 (file)
index 0000000..dec181b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public interface AltosMapCacheListener {
+       public void map_cache_changed(int map_cache);
+}
diff --git a/altoslib/AltosMapInterface.java b/altoslib/AltosMapInterface.java
new file mode 100644 (file)
index 0000000..45398ec
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.net.*;
+
+public interface AltosMapInterface {
+       public abstract AltosMapPath new_path();
+
+       public abstract AltosMapLine new_line();
+
+       public abstract AltosImage load_image(File file) throws Exception;
+
+       public abstract AltosMapMark new_mark(double lat, double lon, int state);
+
+       public abstract AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size);
+
+       public abstract int width();
+
+       public abstract int height();
+
+       public abstract void repaint();
+
+       public abstract void repaint(AltosRectangle damage);
+
+       public abstract void set_zoom_label(String label);
+
+       public abstract void debug(String format, Object ... arguments);
+
+       public abstract void select_object(AltosLatLon latlon);
+}
diff --git a/altoslib/AltosMapLine.java b/altoslib/AltosMapLine.java
new file mode 100644 (file)
index 0000000..09ecb0c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapLine {
+       public AltosLatLon      start, end;
+
+       static public int stroke_width = 6;
+
+       public abstract void paint(AltosMapTransform t);
+
+       private AltosLatLon lat_lon(AltosPointInt pt, AltosMapTransform t) {
+               return t.screen_lat_lon(pt);
+       }
+
+       public void dragged(AltosPointInt pt, AltosMapTransform t) {
+               end = lat_lon(pt, t);
+       }
+
+       public void pressed(AltosPointInt pt, AltosMapTransform t) {
+               start = lat_lon(pt, t);
+               end = null;
+       }
+
+       public String line_dist() {
+               String  format;
+               AltosGreatCircle        g = new AltosGreatCircle(start.lat, start.lon,
+                                                                end.lat, end.lon);
+               double  distance = g.distance;
+
+               if (AltosConvert.imperial_units) {
+                       distance = AltosConvert.meters_to_feet(distance);
+                       if (distance < 10000) {
+                               format = "%4.0fft";
+                       } else {
+                               distance /= 5280;
+                               if (distance < 10)
+                                       format = "%5.3fmi";
+                               else if (distance < 100)
+                                       format = "%5.2fmi";
+                               else if (distance < 1000)
+                                       format = "%5.1fmi";
+                               else
+                                       format = "%5.0fmi";
+                       }
+               } else {
+                       if (distance < 10000) {
+                               format = "%4.0fm";
+                       } else {
+                               distance /= 1000;
+                               if (distance < 100)
+                                       format = "%5.2fkm";
+                               else if (distance < 1000)
+                                       format = "%5.1fkm";
+                               else
+                                       format = "%5.0fkm";
+                       }
+               }
+               return String.format(format, distance);
+       }
+}
diff --git a/altoslib/AltosMapLoader.java b/altoslib/AltosMapLoader.java
new file mode 100644 (file)
index 0000000..8573489
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.lang.Math;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class AltosMapLoader implements AltosMapTileListener, AltosMapStoreListener {
+       AltosMapLoaderListener  listener;
+
+       double  latitude, longitude;
+       int     min_z;
+       int     max_z;
+       int     cur_z;
+       int     all_types;
+       int     cur_type;
+       double  radius;
+
+       int     tiles_loaded_layer;
+       int     tiles_loaded_total;
+       int     tiles_this_layer;
+       int     tiles_total;
+       int     layers_total;
+       int     layers_loaded;
+
+       AltosMap        map;
+
+       int tile_radius(int zoom) {
+               double  delta_lon = AltosMapTransform.lon_from_distance(latitude, radius);
+
+               AltosMapTransform t = new AltosMapTransform(256, 256, zoom + AltosMap.default_zoom, new AltosLatLon(latitude, longitude));
+
+               AltosPointDouble        center = t.point(new AltosLatLon(latitude, longitude));
+               AltosPointDouble        edge = t.point(new AltosLatLon(latitude, longitude + delta_lon));
+
+               int tile_radius = (int) Math.ceil(Math.abs(center.x - edge.x) / AltosMap.px_size);
+
+               return tile_radius;
+       }
+
+       int tiles_per_layer(int zoom) {
+               int     tile_radius = tile_radius(zoom);
+               return (tile_radius * 2 + 1) * (tile_radius * 2 + 1);
+       }
+
+       public void do_load() {
+               tiles_this_layer = tiles_per_layer(cur_z);
+               tiles_loaded_layer = 0;
+               listener.debug("tiles_this_layer %d (zoom %d)\n", tiles_this_layer, cur_z);
+
+               int load_radius = tile_radius(cur_z);
+               int zoom = cur_z + AltosMap.default_zoom;
+               int maptype = cur_type;
+               AltosLatLon load_centre = new AltosLatLon(latitude, longitude);
+               AltosMapTransform transform = new AltosMapTransform(256, 256, zoom, load_centre);
+
+               map.centre(load_centre);
+
+               AltosPointInt   upper_left;
+               AltosPointInt   lower_right;
+
+               AltosPointInt centre = AltosMap.floor(transform.point(load_centre));
+
+               upper_left = new AltosPointInt(centre.x - load_radius * AltosMap.px_size,
+                                              centre.y - load_radius * AltosMap.px_size);
+               lower_right = new AltosPointInt(centre.x + load_radius * AltosMap.px_size,
+                                               centre.y + load_radius * AltosMap.px_size);
+
+
+               for (int y = (int) upper_left.y; y <= lower_right.y; y += AltosMap.px_size) {
+                       for (int x = (int) upper_left.x; x <= lower_right.x; x += AltosMap.px_size) {
+                               listener.debug("Make tile at %d, %d\n", x, y);
+                               AltosPointInt   point = new AltosPointInt(x, y);
+                               AltosLatLon     ul = transform.lat_lon(point);
+                               AltosLatLon     center = transform.lat_lon(new AltosPointDouble(x + AltosMap.px_size/2, y + AltosMap.px_size/2));
+                               AltosMapTile    tile = map.map_interface.new_tile(this, ul, center, zoom, maptype, AltosMap.px_size);
+                               tile.add_store_listener(this);
+                               if (tile.store_status() != AltosMapTile.loading)
+                                       notify_tile(tile, tile.store_status());
+                       }
+               }
+       }
+
+       public int next_type(int start) {
+               int next_type;
+               for (next_type = start;
+                    next_type <= AltosMap.maptype_terrain && (all_types & (1 << next_type)) == 0;
+                    next_type++)
+                       ;
+               return next_type;
+       }
+
+       public void next_load() {
+               int next_type = next_type(cur_type + 1);
+
+               if (next_type > AltosMap.maptype_terrain) {
+                       if (cur_z == max_z) {
+                               return;
+                       } else {
+                               cur_z++;
+                       }
+                       next_type = next_type(0);
+               }
+               cur_type = next_type;
+               do_load();
+       }
+
+       private void start_load() {
+
+               cur_z = min_z;
+               int ntype = 0;
+
+               for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
+                       if ((all_types & (1 << t)) != 0)
+                               ntype++;
+               if (ntype == 0) {
+                       all_types = (1 << AltosMap.maptype_hybrid);
+                       ntype = 1;
+               }
+
+               cur_type = next_type(0);
+
+               for (int z = min_z; z <= max_z; z++)
+                       tiles_total += tiles_per_layer(z);
+
+               layers_total = (max_z - min_z + 1) * ntype;
+               layers_loaded = 0;
+               tiles_loaded_total = 0;
+
+               listener.debug("total tiles %d\n", tiles_total);
+
+               listener.loader_start(tiles_total);
+               do_load();
+       }
+
+       public void load(double latitude, double longitude, int min_z, int max_z, double radius, int all_types) {
+               listener.debug("lat %f lon %f min_z %d max_z %d radius %f all_types %d\n",
+                              latitude, longitude, min_z, max_z, radius, all_types);
+               this.latitude = latitude;
+               this.longitude = longitude;
+               this.min_z = min_z;
+               this.max_z = max_z;
+               this.radius = radius;
+               this.all_types = all_types;
+               start_load();
+       }
+
+       public synchronized void notify_store(AltosMapStore store, int status) {
+               boolean do_next = false;
+               if (status == AltosMapTile.loading)
+                       return;
+
+               if (layers_loaded >= layers_total)
+                       return;
+
+               ++tiles_loaded_total;
+               ++tiles_loaded_layer;
+               listener.debug("total %d layer %d\n", tiles_loaded_total, tiles_loaded_layer);
+
+               if (tiles_loaded_layer == tiles_this_layer) {
+                       ++layers_loaded;
+                       listener.debug("%d layers loaded\n", layers_loaded);
+                       if (layers_loaded == layers_total) {
+                               listener.loader_done(tiles_total);
+                               return;
+                       } else {
+                               do_next = true;
+                       }
+               }
+               listener.loader_notify(tiles_loaded_total,
+                                      tiles_total, store.file.toString());
+               if (do_next)
+                       next_load();
+       }
+
+       public synchronized void notify_tile(AltosMapTile tile, int status) {
+               notify_store(tile.store, status);
+       }
+
+       public AltosMapCache cache() { return map.cache(); }
+
+       public AltosMapLoader(AltosMap map, AltosMapLoaderListener listener) {
+               this.map = map;
+               this.listener = listener;
+       }
+}
diff --git a/altoslib/AltosMapLoaderListener.java b/altoslib/AltosMapLoaderListener.java
new file mode 100644 (file)
index 0000000..0b41d9f
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public interface AltosMapLoaderListener {
+       public abstract void loader_start(int max);
+
+       public abstract void loader_notify(int cur, int max, String name);
+
+       public abstract void loader_done(int max);
+
+       public abstract void debug(String format, Object ... arguments);
+}
diff --git a/altoslib/AltosMapMark.java b/altoslib/AltosMapMark.java
new file mode 100644 (file)
index 0000000..cc28f43
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapMark {
+
+       public AltosLatLon      lat_lon;
+       public int              state;
+
+       static public int stroke_width = 6;
+
+       public abstract void paint(AltosMapTransform t);
+
+       public AltosMapMark (double lat, double lon, int state) {
+               lat_lon = new AltosLatLon(lat, lon);
+               this.state = state;
+       }
+}
diff --git a/altoslib/AltosMapPath.java b/altoslib/AltosMapPath.java
new file mode 100644 (file)
index 0000000..6967f47
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public abstract class AltosMapPath {
+
+       public LinkedList<AltosMapPathPoint>    points = new LinkedList<AltosMapPathPoint>();
+       public AltosMapPathPoint                last_point = null;
+
+       static public int stroke_width = 6;
+
+       public abstract void paint(AltosMapTransform t);
+
+       public AltosMapRectangle add(double lat, double lon, int state) {
+               AltosMapPathPoint               point = new AltosMapPathPoint(new AltosLatLon (lat, lon), state);
+               AltosMapRectangle       rect = null;
+
+               if (!point.equals(last_point)) {
+                       if (last_point != null)
+                               rect = new AltosMapRectangle(last_point.lat_lon, point.lat_lon);
+                       points.add (point);
+                       last_point = point;
+               }
+               return rect;
+       }
+
+       public void clear () {
+               points = new LinkedList<AltosMapPathPoint>();
+       }
+}
diff --git a/altoslib/AltosMapPathPoint.java b/altoslib/AltosMapPathPoint.java
new file mode 100644 (file)
index 0000000..0af9899
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMapPathPoint {
+       public AltosLatLon      lat_lon;
+       public int              state;
+
+       public int hashCode() {
+               return lat_lon.hashCode() ^ state;
+       }
+
+       public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+
+               if (!(o instanceof AltosMapPathPoint))
+                       return false;
+
+               AltosMapPathPoint other = (AltosMapPathPoint) o;
+
+               return lat_lon.equals(other.lat_lon) && state == other.state;
+       }
+
+       public AltosMapPathPoint(AltosLatLon lat_lon, int state) {
+               this.lat_lon = lat_lon;
+               this.state = state;
+       }
+}
+
diff --git a/altoslib/AltosMapRectangle.java b/altoslib/AltosMapRectangle.java
new file mode 100644 (file)
index 0000000..9fdab1a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public class AltosMapRectangle {
+       AltosLatLon     ul, lr;
+
+       public AltosMapRectangle(AltosLatLon a, AltosLatLon b) {
+               double  ul_lat, ul_lon;
+               double  lr_lat, lr_lon;
+
+               if (a.lat > b.lat) {
+                       ul_lat = a.lat;
+                       lr_lat = b.lat;
+               } else {
+                       ul_lat = b.lat;
+                       lr_lat = a.lat;
+               }
+               if (a.lon < b.lon) {
+                       ul_lon = a.lon;
+                       lr_lon = b.lon;
+               } else {
+                       ul_lon = b.lon;
+                       lr_lon = a.lon;
+               }
+
+               ul = new AltosLatLon(ul_lat, ul_lon);
+               lr = new AltosLatLon(lr_lat, lr_lon);
+       }
+}
diff --git a/altoslib/AltosMapStore.java b/altoslib/AltosMapStore.java
new file mode 100644 (file)
index 0000000..b24fabc
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.net.*;
+import java.util.*;
+
+public class AltosMapStore {
+       String                                  url;
+       public File                             file;
+       LinkedList<AltosMapStoreListener>       listeners = new LinkedList<AltosMapStoreListener>();
+
+       int                                     status;
+
+       public int status() {
+               return status;
+       }
+
+       public synchronized void add_listener(AltosMapStoreListener listener) {
+               if (!listeners.contains(listener))
+                       listeners.add(listener);
+       }
+
+       public synchronized void remove_listener(AltosMapStoreListener listener) {
+               listeners.remove(listener);
+       }
+
+       private synchronized void notify_listeners(int status) {
+               this.status = status;
+               for (AltosMapStoreListener listener : listeners)
+                       listener.notify_store(this, status);
+       }
+
+       static Object   forbidden_lock = new Object();
+       static long     forbidden_time;
+       static boolean  forbidden_set;
+
+       private int fetch_url() {
+               URL u;
+
+               try {
+                       u = new URL(url);
+               } catch (java.net.MalformedURLException e) {
+                       return AltosMapTile.bad_request;
+               }
+
+               byte[] data;
+               URLConnection uc = null;
+               try {
+                       uc = u.openConnection();
+                       String type = uc.getContentType();
+                       int contentLength = uc.getContentLength();
+                       if (uc instanceof HttpURLConnection) {
+                               int response = ((HttpURLConnection) uc).getResponseCode();
+                               switch (response) {
+                               case HttpURLConnection.HTTP_FORBIDDEN:
+                               case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
+                               case HttpURLConnection.HTTP_UNAUTHORIZED:
+                                       synchronized (forbidden_lock) {
+                                               forbidden_time = System.nanoTime();
+                                               forbidden_set = true;
+                                               return AltosMapTile.forbidden;
+                                       }
+                               }
+                       }
+                       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 AltosMapTile.failed;
+
+               } catch (IOException e) {
+                       return AltosMapTile.failed;
+               }
+
+               try {
+                       FileOutputStream out = new FileOutputStream(file);
+                       out.write(data);
+                       out.flush();
+                       out.close();
+               } catch (FileNotFoundException e) {
+                       return AltosMapTile.bad_request;
+               } catch (IOException e) {
+                       if (file.exists())
+                               file.delete();
+                       return AltosMapTile.bad_request;
+               }
+               return AltosMapTile.success;
+       }
+
+       static Object   fetch_lock = new Object();
+
+       static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
+       static final long       google_maps_ratelimit_ms = 1200;
+
+       static Object   loader_lock = new Object();
+
+       static LinkedList<AltosMapStore> waiting = new LinkedList<AltosMapStore>();
+       static LinkedList<AltosMapStore> running = new LinkedList<AltosMapStore>();
+
+       static final int concurrent_loaders = 128;
+
+       static void start_loaders() {
+               while (!waiting.isEmpty() && running.size() < concurrent_loaders) {
+                       AltosMapStore   s = waiting.remove();
+                       running.add(s);
+                       Thread lt = s.make_loader_thread();
+                       lt.start();
+               }
+       }
+
+       void finish_loader() {
+               synchronized(loader_lock) {
+                       running.remove(this);
+                       start_loaders();
+               }
+       }
+
+       void add_loader() {
+               synchronized(loader_lock) {
+                       waiting.add(this);
+                       start_loaders();
+               }
+       }
+
+       class loader implements Runnable {
+
+               public void run() {
+                       try {
+                               if (file.exists()) {
+                                       notify_listeners(AltosMapTile.success);
+                                       return;
+                               }
+
+                               synchronized(forbidden_lock) {
+                                       if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
+                                               notify_listeners(AltosMapTile.forbidden);
+                                               return;
+                                       }
+                               }
+
+                               int new_status;
+
+                               if (!AltosVersion.has_google_maps_api_key()) {
+                                       synchronized (fetch_lock) {
+                                               long startTime = System.nanoTime();
+                                               new_status = fetch_url();
+                                               if (new_status == AltosMapTile.success) {
+                                                       long duration_ms = (System.nanoTime() - startTime) / 1000000;
+                                                       if (duration_ms < google_maps_ratelimit_ms) {
+                                                               try {
+                                                                       Thread.sleep(google_maps_ratelimit_ms - duration_ms);
+                                                               } catch (InterruptedException e) {
+                                                                       Thread.currentThread().interrupt();
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               } else {
+                                       new_status = fetch_url();
+                               }
+                               notify_listeners(new_status);
+                       } finally {
+                               finish_loader();
+                       }
+               }
+       }
+
+       private Thread make_loader_thread() {
+               return new Thread(new loader());
+       }
+
+       private void load() {
+               add_loader();
+       }
+
+       private AltosMapStore (String url, File file) {
+               this.url = url;
+               this.file = file;
+
+               if (file.exists())
+                       status = AltosMapTile.success;
+               else {
+                       status = AltosMapTile.loading;
+                       load();
+               }
+       }
+
+       public int hashCode() {
+               return url.hashCode();
+       }
+
+       public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+
+               if (!(o instanceof AltosMapStore))
+                       return false;
+
+               AltosMapStore other = (AltosMapStore) o;
+               return url.equals(other.url);
+       }
+
+       static HashMap<String,AltosMapStore> stores = new HashMap<String,AltosMapStore>();
+
+       public static AltosMapStore get(String url, File file) {
+               AltosMapStore   store;
+               synchronized(stores) {
+                       if (stores.containsKey(url)) {
+                               store = stores.get(url);
+                       } else {
+                               store = new AltosMapStore(url, file);
+                               stores.put(url, store);
+                       }
+               }
+               return store;
+       }
+}
diff --git a/altoslib/AltosMapStoreListener.java b/altoslib/AltosMapStoreListener.java
new file mode 100644 (file)
index 0000000..cef4f92
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public interface AltosMapStoreListener {
+       abstract void notify_store(AltosMapStore store, int status);
+}
diff --git a/altoslib/AltosMapTile.java b/altoslib/AltosMapTile.java
new file mode 100644 (file)
index 0000000..a6ba5da
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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 org.altusmetrum.altoslib_8;
+
+import java.io.*;
+import java.util.*;
+
+public abstract class AltosMapTile implements AltosFontListener {
+       AltosMapTileListener    listener;
+       public AltosLatLon      upper_left, center;
+       public int              px_size;
+       int             zoom;
+       int             maptype;
+       int             scale;
+       public AltosMapStore    store;
+       public AltosMapCache    cache;
+       public int      status;
+
+       static public final int success = 0;
+       static public final int loading = 1;
+       static public final int failed = 2;
+       static public final int bad_request = 3;
+       static public final int forbidden = 4;
+
+       private File map_file() {
+               double lat = center.lat;
+               double lon = center.lon;
+               char chlat = lat < 0 ? 'S' : 'N';
+               char chlon = lon < 0 ? 'W' : 'E';
+
+               if (lat < 0) lat = -lat;
+               if (lon < 0) lon = -lon;
+               String maptype_string = String.format("%s-", AltosMap.maptype_names[maptype]);
+               String format_string;
+               if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
+                       format_string = "jpg";
+               else
+                       format_string = "png";
+               return new File(AltosPreferences.mapdir(),
+                               String.format("map-%c%.6f,%c%.6f-%s%d%s.%s",
+                                             chlat, lat, chlon, lon, maptype_string, zoom, scale == 1 ? "" : String.format("-%d", scale), format_string));
+       }
+
+       private String map_url() {
+               String format_string;
+               int z = zoom;
+
+               if (maptype == AltosMap.maptype_hybrid || maptype == AltosMap.maptype_satellite || maptype == AltosMap.maptype_terrain)
+                       format_string = "jpg";
+               else
+                       format_string = "png32";
+
+               for (int s = 1; s < scale; s <<= 1)
+                       z--;
+
+               if (AltosVersion.has_google_maps_api_key())
+                       return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s&key=%s",
+                                            center.lat, center.lon, z, px_size/scale, px_size/scale, scale, AltosMap.maptype_names[maptype], format_string, AltosVersion.google_maps_api_key);
+               else
+                       return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&scale=%d&sensor=false&maptype=%s&format=%s",
+                                            center.lat, center.lon, z, px_size/scale, px_size/scale, AltosMap.maptype_names[maptype], format_string);
+       }
+
+       public void font_size_changed(int font_size) {
+       }
+
+       public void set_status(int status) {
+               this.status = status;
+               listener.notify_tile(this, status);
+       }
+
+       public void notify_image(AltosImage image) {
+               listener.notify_tile(this, status);
+       }
+
+       public int store_status() {
+               return store.status();
+       }
+
+       public void add_store_listener(AltosMapStoreListener listener) {
+               store.add_listener(listener);
+       }
+
+       public void remove_store_listener(AltosMapStoreListener listener) {
+               store.remove_listener(listener);
+       }
+
+       public abstract void paint(AltosMapTransform t);
+
+       public AltosMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size, int scale) {
+               this.listener = listener;
+               this.upper_left = upper_left;
+               this.cache = listener.cache();
+
+               while (center.lon < -180.0)
+                       center.lon += 360.0;
+               while (center.lon > 180.0)
+                       center.lon -= 360.0;
+
+               this.center = center;
+               this.zoom = zoom;
+               this.maptype = maptype;
+               this.px_size = px_size;
+               this.scale = scale;
+
+               status = AltosMapTile.loading;
+               store = AltosMapStore.get(map_url(), map_file());
+       }
+
+       public AltosMapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+               this(listener, upper_left, center, zoom, maptype, px_size, 1);
+       }
+}
diff --git a/altoslib/AltosMapTileListener.java b/altoslib/AltosMapTileListener.java
new file mode 100644 (file)
index 0000000..c02483b
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public interface AltosMapTileListener {
+       abstract public void notify_tile(AltosMapTile tile, int status);
+
+       abstract public AltosMapCache cache();
+}
diff --git a/altoslib/AltosMapTransform.java b/altoslib/AltosMapTransform.java
new file mode 100644 (file)
index 0000000..b8f2910
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import java.io.*;
+import java.lang.Math;
+import java.util.*;
+import java.util.concurrent.*;
+
+public class AltosMapTransform {
+
+       double  scale_x, scale_y;
+
+       double  offset_x, offset_y;
+
+       public AltosLatLon lat_lon (AltosPointDouble point) {
+               double lat, lon;
+               double rads;
+
+               lon = point.x/scale_x;
+               rads = 2 * Math.atan(Math.exp(-point.y/scale_y));
+               lat = Math.toDegrees(rads - Math.PI/2);
+
+               return new AltosLatLon(lat,lon);
+       }
+
+       public AltosLatLon lat_lon (AltosPointInt point) {
+               return lat_lon(new AltosPointDouble(point.x, point.y));
+       }
+
+       public AltosPointDouble screen_point(AltosPointInt screen) {
+               return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y);
+       }
+
+       public AltosPointDouble screen_point(AltosPointDouble screen) {
+               return new AltosPointDouble(screen.x + offset_x, screen.y + offset_y);
+       }
+
+       public double hypot(AltosLatLon a, AltosLatLon b) {
+               AltosPointDouble        a_pt = point(a);
+               AltosPointDouble        b_pt = point(b);
+
+               return Math.hypot(a_pt.x - b_pt.x, a_pt.y - b_pt.y);
+       }
+
+       public AltosLatLon screen_lat_lon(AltosPointInt screen) {
+               return lat_lon(screen_point(screen));
+       }
+
+       public AltosLatLon screen_lat_lon(AltosPointDouble screen) {
+               return lat_lon(screen_point(screen));
+       }
+
+       public AltosPointDouble point(AltosLatLon lat_lon) {
+               double x, y;
+               double e;
+
+               x = lat_lon.lon * scale_x;
+
+               e = Math.sin(Math.toRadians(lat_lon.lat));
+               e = Math.max(e,-(1-1.0E-15));
+               e = Math.min(e,  1-1.0E-15 );
+
+               y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
+
+               return new AltosPointDouble(x, y);
+       }
+
+       public AltosPointDouble screen(AltosPointDouble point) {
+               return new AltosPointDouble(point.x - offset_x, point.y - offset_y);
+       }
+
+       public AltosPointInt screen(AltosPointInt point) {
+               return new AltosPointInt((int) (point.x - offset_x + 0.5),
+                                        (int) (point.y - offset_y + 0.5));
+       }
+
+       public AltosRectangle screen(AltosMapRectangle map_rect) {
+               AltosPointDouble        ul = screen(map_rect.ul);
+               AltosPointDouble        lr = screen(map_rect.lr);
+
+               return new AltosRectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y));
+       }
+
+       public AltosPointDouble screen(AltosLatLon lat_lon) {
+               return screen(point(lat_lon));
+       }
+
+       private boolean has_location;
+
+       public boolean has_location() {
+               return has_location;
+       }
+
+       public AltosMapTransform(int width, int height, int zoom, AltosLatLon centre_lat_lon) {
+               scale_x = 256/360.0 * Math.pow(2, zoom);
+               scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
+
+               AltosPointDouble centre_pt = point(centre_lat_lon);
+
+               has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0);
+               offset_x = centre_pt.x - width / 2.0;
+               offset_y = centre_pt.y - height / 2.0;
+       }
+
+       public static double lon_from_distance(double lat, double distance) {
+               double c = AltosGreatCircle.earth_radius * Math.cos(lat * Math.PI / 180) * 2 * Math.PI;
+
+               if (c < 10)
+                       return 0;
+               return distance/c * 360.0;
+       }
+}
diff --git a/altoslib/AltosMapZoomListener.java b/altoslib/AltosMapZoomListener.java
new file mode 100644 (file)
index 0000000..aa06730
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public interface AltosMapZoomListener {
+       abstract public void zoom_changed(int zoom);
+}
index c0b94b8cc7ef7dcbdaac671dae6b70eee6e01650..8b1ac088d7dcf5db97efe3ccd899087964290622 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.*;
 
index 2bd4ba8fe717d194dc9eb1f1006a375b0ad0edda..e7ca1ba34fb1dd9289efe3a0c31801783504ce0f 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.*;
 import java.io.*;
index 77410a258c348d966eca946939eefa8054505c4f..6ef2a1425133563009715c8eac4d673de8787ad2 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosNoSymbol extends Exception {
        public AltosNoSymbol(String name) {
index 8cdde75006c645fe9a64904757dc6c5d936bce39..1c2e2aab2258eb8bc6394aa50d231afa93713aaf 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosOrient extends AltosUnits {
 
index 2fb69c15ce40de247537c22377f422d15afcbf18..8f624b96b5ac3faca117cac564dd93828636f245 100644 (file)
@@ -15,8 +15,9 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
+import java.util.*;
 import java.text.*;
 
 public class AltosParse {
@@ -40,11 +41,23 @@ public class AltosParse {
                }
        }
 
-       public static double parse_double(String v) throws ParseException {
+       static NumberFormat nf_locale = NumberFormat.getInstance();
+
+       static NumberFormat nf_net = NumberFormat.getInstance(Locale.ROOT);
+
+       public static double parse_double_locale(String str) throws ParseException {
                try {
-                       return Double.parseDouble(v);
-               } catch (NumberFormatException e) {
-                       throw new ParseException("error parsing double " + v, 0);
+                       return nf_locale.parse(str.trim()).doubleValue();
+               } catch (ParseException pe) {
+                       throw new ParseException("error parsing double " + str, 0);
+               }
+       }
+
+       public static double parse_double_net(String str) throws ParseException {
+               try {
+                       return nf_net.parse(str.trim()).doubleValue();
+               } catch (ParseException pe) {
+                       throw new ParseException("error parsing double " + str, 0);
                }
        }
 
diff --git a/altoslib/AltosPointDouble.java b/altoslib/AltosPointDouble.java
new file mode 100644 (file)
index 0000000..ecfcbea
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public class AltosPointDouble {
+       public double   x, y;
+
+       public int hashCode() {
+               return new Double(x).hashCode() ^ new Double(y).hashCode();
+       }
+
+       public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+
+               if (!(o instanceof AltosPointDouble))
+                       return false;
+
+               AltosPointDouble n = (AltosPointDouble) o;
+
+               return n.x == x && n.y == y;
+       }
+
+       public AltosPointDouble(double x, double y) {
+               this.x = x;
+               this.y = y;
+       }
+
+       public AltosPointDouble(int x, int y) {
+               this.x = x;
+               this.y = y;
+       }
+
+       public AltosPointDouble(AltosPointInt p) {
+               this.x = p.x;
+               this.y = p.y;
+       }
+}
diff --git a/altoslib/AltosPointInt.java b/altoslib/AltosPointInt.java
new file mode 100644 (file)
index 0000000..e690aa4
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public class AltosPointInt {
+       public int      x, y;
+
+       public int hashCode() {
+               return  x ^ y;
+       }
+
+       public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+
+               if (!(o instanceof AltosPointInt))
+                       return false;
+
+               AltosPointInt n = (AltosPointInt) o;
+
+               return n.x == x && n.y == y;
+       }
+
+       public AltosPointInt(int x, int y) {
+               this.x = x;
+               this.y = y;
+       }
+
+       public AltosPointInt(double x, double y) {
+               this.x = (int) (x + 0.5);
+               this.y = (int) (y + 0.5);
+       }
+
+       public AltosPointInt(AltosPointDouble pt_d) {
+               this.x = (int) (pt_d.x + 0.5);
+               this.y = (int) (pt_d.y + 0.5);
+       }
+}
index 5aa45d3f5d992745bf03afceddd983bde97ecb37..a1e76834541f0e02f600c0c5cf1bffee5b0ad07a 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
+import java.text.*;
 
 public class AltosPreferences {
        public static AltosPreferencesBackend backend = null;
@@ -42,7 +43,9 @@ public class AltosPreferences {
        public final static String logfilePreferenceFormat = "LOGFILE-%d";
 
        /* state preference name */
+       public final static String statePreferenceHead = "STATE-";
        public final static String statePreferenceFormat = "STATE-%d";
+       public final static String statePreferenceLatest = "STATE-LATEST";
 
        /* voice preference name */
        public final static String voicePreference = "VOICE";
@@ -118,6 +121,13 @@ public class AltosPreferences {
 
        public final static String      unitsPreference = "IMPERIAL-UNITS";
 
+       /* Maps cache size preference name */
+       final static String mapCachePreference = "MAP-CACHE";
+
+       static LinkedList<AltosMapCacheListener> map_cache_listeners;
+
+       public static int map_cache = 9;
+
        public static AltosFrequency[] load_common_frequencies() {
                AltosFrequency[] frequencies = null;
                boolean existing = false;
@@ -208,6 +218,9 @@ public class AltosPreferences {
                common_frequencies = load_common_frequencies();
 
                AltosConvert.imperial_units = backend.getBoolean(unitsPreference, false);
+
+               map_cache = backend.getInt(mapCachePreference, 9);
+               map_cache_listeners = new LinkedList<AltosMapCacheListener>();
        }
 
        public static void flush_preferences() {
@@ -350,12 +363,43 @@ public class AltosPreferences {
 
                        synchronized(backend) {
                                backend.putBytes(String.format(statePreferenceFormat, serial), bytes);
+                               backend.putInt(statePreferenceLatest, serial);
                                flush_preferences();
                        }
                } catch (IOException ie) {
                }
        }
 
+       public static ArrayList<Integer> list_states() {
+               String[]                keys = backend.keys();
+               ArrayList<Integer>      states = new ArrayList<Integer>();
+
+               for (String key : keys) {
+                       if (key.startsWith(statePreferenceHead)) {
+                               try {
+                                       int serial = AltosParse.parse_int(key.substring(statePreferenceHead.length()));
+                                       states.add(serial);
+                               } catch (ParseException pe) {
+                               }
+                       }
+               }
+               return states;
+       }
+
+       public static void remove_state(int serial) {
+               synchronized(backend) {
+                       backend.remove(String.format(statePreferenceFormat, serial));
+               }
+       }
+
+       public static int latest_state() {
+               int     latest = 0;
+               synchronized (backend) {
+                       latest = backend.getInt(statePreferenceLatest, 0);
+               }
+               return latest;
+       }
+
        public static AltosSavedState state(int serial) {
                byte[] bytes = null;
 
@@ -548,4 +592,33 @@ public class AltosPreferences {
                        units_listeners.remove(l);
                }
        }
+
+
+       public static void register_map_cache_listener(AltosMapCacheListener l) {
+               synchronized(backend) {
+                       map_cache_listeners.add(l);
+               }
+       }
+
+       public static void unregister_map_cache_listener(AltosMapCacheListener l) {
+               synchronized (backend) {
+                       map_cache_listeners.remove(l);
+               }
+       }
+
+       public static void set_map_cache(int new_map_cache) {
+               synchronized(backend) {
+                       map_cache = new_map_cache;
+                       backend.putInt(mapCachePreference, map_cache);
+                       flush_preferences();
+                       for (AltosMapCacheListener l: map_cache_listeners)
+                               l.map_cache_changed(map_cache);
+               }
+       }
+
+       public static int map_cache() {
+               synchronized(backend) {
+                       return map_cache;
+               }
+       }
 }
index c83eaa478c0ecceca4dbf9278b316221e8b63a87..3154a519205510e4b1a312f96bc0041af3e97900 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.File;
 
index 7a92c2d07c1b6961800d078cbb63bf349f8eb1a5..74ab42b54e03912bce2fc701346bccb51fbfacdb 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index 502e34def0d386270237cfe84c2423b609ea03a9..9b7e79f33048ea07c7ce4ce96c7b9a948c427c9e 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.*;
 import java.text.*;
index 4ad1f3d683cf2f484c486305c8ab3074d805257a..f78b50653b4e474849418db7cbfb7575517f3fb5 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosQuaternion {
        double  r;              /* real bit */
diff --git a/altoslib/AltosRectangle.java b/altoslib/AltosRectangle.java
new file mode 100644 (file)
index 0000000..1b1cc9f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+public class AltosRectangle {
+       public int      x, y, width, height;
+
+       public AltosRectangle(int x, int y, int w, int h) {
+               this.x = x;
+               this.y = y;
+               this.width = w;
+               this.height = h;
+       }
+}
index 98f0e7d59f45a3a59f1d5d8feecd3cec97f68597..9419ec93060856e23d49dd1147b90af485b9d755 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
@@ -40,7 +40,7 @@ public class AltosReplayReader extends AltosFlightReader {
        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 && state.time_change > 0)
-                       Thread.sleep((int) (Math.min(state.time_change,10) * 100));
+                       Thread.sleep((int) (Math.min(state.time_change,10) * 1000));
                state.set_received_time(System.currentTimeMillis());
        }
 
index c93a01c3b1bfda4fb783f06ce1359f9dba028d46..28134aebb4c02af0588d5d780e4a6967d676bc10 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index 49225f77db80d34cf82199027944faebddc5fff4..9771c1660f7716718a5f4756ae16f39b50a92e86 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosRotation {
        private AltosQuaternion         rotation;
index 552e453376bd1269f211c8a305e3dce0b0a90479..e2ca9f73f274ad80a476a0dea3ef13a3de7733f3 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index 83be4be16ac938bc294790572c6bf529adae5eb3..e2988b566e8f534cbf71aed7d5330a8071847bc6 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
index cb8356a199f7a75c55069c1edd6d9efb7483e424..e8b7b0445d81519567ba7c7edf34b08fef37c288 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.TimeoutException;
 
index 9d5649aa56cee7b959b2f07d2d8e5c236a724981..9be29ae012ba998b657c7559e280865c6efd71ad 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.TimeoutException;
 
index a3c2a033abbdb279fcefd1ea3af163b9e13f1fe0..1f71302e441a3e5cd6e09889ecdfca27bd1d9c70 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.TimeoutException;
 
index 39592b50f27e72da555ca68a48c366b4bb86a357..c00e00d5738f001e2c67cb4231d55fa535edcf71 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.TimeoutException;
 
index 32607fbab9585704ec0c407843c90cf44e4304ba..64cce64092dda752fbd2eec83af1982707b1f947 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.TimeoutException;
 
index c82ba93c92eb1890b531dd9183e8c993da361862..4957b94d9433e4a382cb197b0812e10c28aa2a78 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.TimeoutException;
 
index 0fc70e7191f60a9aa8833abe37f3c5eb8b0df610..9b2db2ffda37aa27375560bad83bee4274e00233 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.util.concurrent.TimeoutException;
 
index b714412fa49e77b317f08ca23bdc479e1c8949e3..b9fca82b5cb02c648613a6d023f3522f3a33642c 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosSpeed extends AltosUnits {
 
index d363027cb214242c88799fc98bc8d4cdedd58291..4edae54adaef19185e2735abe0013a68d9e29fc7 100644 (file)
@@ -19,7 +19,7 @@
  * Track flight state from telemetry or eeprom data stream
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 
@@ -31,8 +31,9 @@ public class AltosState implements Cloneable, Serializable {
 
        public int set;
 
+       static final double filter_len = 2.0;
        static final double ascent_filter_len = 0.5;
-       static final double descent_filter_len = 0.5;
+       static final double descent_filter_len = 5.0;
 
        /* derived data */
 
@@ -64,8 +65,10 @@ public class AltosState implements Cloneable, Serializable {
                }
 
                void set_filtered(double new_value, double time) {
-                       if (prev_value != AltosLib.MISSING)
-                               new_value = (prev_value * 15.0 + new_value) / 16.0;
+                       if (prev_value != AltosLib.MISSING) {
+                               double f = 1/Math.exp((time - prev_set_time) / filter_len);
+                               new_value = f * new_value + (1-f) * prev_value;
+                       }
                        set(new_value, time);
                }
 
@@ -1040,6 +1043,10 @@ public class AltosState implements Cloneable, Serializable {
                return AltosLib.state_name(state);
        }
 
+       public void set_product(String product) {
+               this.product = product;
+       }
+
        public void set_state(int state) {
                if (state != AltosLib.ao_flight_invalid) {
                        this.state = state;
index 3b58fc32e8485ce430a0fe8894e983d83039607d..5533468bd4db9ad2252418504944d2d8af229604 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 93b9f1c03f4c96fba2552df7f84353ce955e0413..ed2e1826399d2d67c6b931f5a8968d37a0b4f747 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public interface AltosStateUpdate {
        public void     update_state(AltosState state) throws InterruptedException;
index 449384d528ef5c8ed66dc7fcce3ec2b78942a28b..4c973cd95466519482d88a0d503bc7643589b9f5 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.text.*;
 
index 8c922b03fa24b1340870e865ce176ba7be9a12eb..c4cd36b254981c0f7e457c4135a236bfcedf713b 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 
 public class AltosTelemetryConfiguration extends AltosTelemetryStandard {
index 738e4dd7e4a636215703f957a617bcae1002d96c..d2275f542df83171af40de4ec22d4c77774c90b8 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 131389d58ece005a1ec8c6d2626dd7031e3510d3..e18f7edaca45402bcc93b69b4d25007a1521616a 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.io.*;
 import java.util.*;
index 923d139e911290187aeb65b594eb2223ae09d34b..f8e72c863e0ce22b6e9651c0c9e138c221fef5c2 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.text.*;
 
index 85da27d5b8642aec301eef9fc895d2ed96432f59..9341d0032dae2c8f4bea7661f575598637127dc9 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 
 public class AltosTelemetryLocation extends AltosTelemetryStandard {
index e8c02e9be8533da4ac8bc762acff31008171c263..d32422547b7e55c7139eec3968c0d358e12e419f 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 import java.text.*;
 import java.util.HashMap;
 
index 2b80df4a91f673b26d2e83b0efa2dc32d09ba0a2..2af8d931d857f873b39fde3c972d4a880acbdc98 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosTelemetryMegaData extends AltosTelemetryStandard {
        int     state;
index a01c0826ab27a11d73f62d236a97696285cec49e..77a76cd7c9b2bdf12e916ae35df3d6d5f914e4ed 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosTelemetryMegaSensor extends AltosTelemetryStandard {
        int     accel;
index e53f12838836148a604dffe6d4616b204ec671a0..17a3410a422896dcb47d71386f992af9957b6c96 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 
 public class AltosTelemetryMetrumData extends AltosTelemetryStandard {
index 415b00a6e56e22d73decb024ececcb54810a123a..71fbd54497bb25e522d06fd1f1b2ec1029ea21ab 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 
 public class AltosTelemetryMetrumSensor extends AltosTelemetryStandard {
index 02537c9c0843fcb80c8268507e16c2d1e9344cda..c9fda5b800407267dd7daf2fa7ca3c3993d54f97 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 
 public class AltosTelemetryMini extends AltosTelemetryStandard {
index 4254fc8391655d01d7f539d39df4f3c90146a9ab..843ca4ebcc2e4fbc097262f9c4bd7e5482b48907 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosTelemetryRaw extends AltosTelemetryStandard {
        public AltosTelemetryRaw(int[] bytes) {
index 908fb5c7480c451f6c7692cd4f3300c6bc638511..cbbdceffec31f2895ea09a48fa3fd0ea28678dde 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 import java.text.*;
 import java.io.*;
index 474789ba6bcd97f3e99f9f56531959de6d250723..2f4e335414c392fe5765b369ffe5c71c01b0f1dd 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosTelemetrySatellite extends AltosTelemetryStandard {
        int             channels;
index b0c84fd358b80fe9c2cbc7ce05b37ddef269e542..27cf22f0844010093820cd396772a9c5d5c85c50 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 
 public class AltosTelemetrySensor extends AltosTelemetryStandard {
index fb8a162e6a00f6e1fae1de2aae17ef4829a2c39f..2756182616f7cec6a2d79ac0d9025854feb3ffa2 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public abstract class AltosTelemetryStandard extends AltosTelemetry {
        int[]   bytes;
index 494f4e3e5c73d6f75ff7fb0b0d08dffa0e66fc9e..b4d465a0411026e86468834fd78555368f622df4 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosTemperature extends AltosUnits {
 
index f6e34e773ed36e153d46dd8e6d8be2a0972d4624..6b4fff0c2b0a80bd928f1c66562b7236516f014f 100644 (file)
@@ -15,7 +15,9 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
+
+import java.text.*;
 
 public abstract class AltosUnits {
 
@@ -29,13 +31,22 @@ public abstract class AltosUnits {
 
        public abstract int show_fraction(int width, boolean imperial_units);
 
-       public double parse(String s, boolean imperial_units) throws NumberFormatException {
-               double v = Double.parseDouble(s);
+       public double parse_locale(String s, boolean imperial_units) throws ParseException {
+               double v = AltosParse.parse_double_locale(s);
+               return inverse(v, imperial_units);
+       }
+
+       public double parse_net(String s, boolean imperial_units) throws ParseException {
+               double v = AltosParse.parse_double_net(s);
                return inverse(v, imperial_units);
        }
 
-       public double parse(String s) throws NumberFormatException {
-               return parse(s, AltosConvert.imperial_units);
+       public double parse_locale(String s) throws ParseException {
+               return parse_locale(s, AltosConvert.imperial_units);
+       }
+
+       public double parse_net(String s) throws ParseException {
+               return parse_net(s, AltosConvert.imperial_units);
        }
 
        public double value(double v) {
@@ -105,4 +116,4 @@ public abstract class AltosUnits {
        public String say_units(double v) {
                return say_units(v, AltosConvert.imperial_units);
        }
-}
\ No newline at end of file
+}
index 1b66eb45a349d2021ef82a6d8b6e15c59058a9a6..0e31492e1891d5b9606f7953c921fb6cafcd4487 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public interface AltosUnitsListener {
        public void units_changed(boolean imperial_units);
diff --git a/altoslib/AltosVersion.java.in b/altoslib/AltosVersion.java.in
new file mode 100644 (file)
index 0000000..89ed400
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * 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_8;
+
+public class AltosVersion {
+       public final static String version = "@VERSION@";
+
+       public final static String google_maps_api_key = @GOOGLEKEY@;
+
+       public static boolean has_google_maps_api_key() {
+               return google_maps_api_key != null && google_maps_api_key.length() > 1;
+       }
+}
index 986d74b40f7694208df071f1b6cc4fb486bf3747..fb6f41ee622d37ea71cc870e04d9820796563eab 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public class AltosVoltage extends AltosUnits {
 
index b125bd872c681707dc0b26943c912550c49b15b1..1dcd3391fe9b373e28503895fd35636bcd49e014 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
 
 public interface AltosWriter {
 
index c640c69cadf1fffda78cecad47ed7dac9ca0529d..a6b178fa167fcbee9efcd110222432c5c7abf8ab 100644 (file)
@@ -128,7 +128,35 @@ altoslib_JAVA = \
        AltosPyro.java \
        AltosWriter.java \
        AltosQuaternion.java \
-       AltosRotation.java
+       AltosRotation.java \
+       AltosImage.java \
+       AltosLatLon.java \
+       AltosMap.java \
+       AltosMapCache.java \
+       AltosMapCacheListener.java \
+       AltosMapInterface.java \
+       AltosMapLine.java \
+       AltosMapMark.java \
+       AltosMapPath.java \
+       AltosMapPathPoint.java \
+       AltosMapRectangle.java \
+       AltosMapStore.java \
+       AltosMapStoreListener.java \
+       AltosMapTile.java \
+       AltosMapTileListener.java \
+       AltosMapTransform.java \
+       AltosMapZoomListener.java \
+       AltosPointDouble.java \
+       AltosPointInt.java \
+       AltosRectangle.java \
+       AltosFlightDisplay.java \
+       AltosFontListener.java \
+       AltosLaunchSite.java \
+       AltosLaunchSiteListener.java \
+       AltosLaunchSites.java \
+       AltosMapLoaderListener.java \
+       AltosMapLoader.java \
+       AltosVersion.java
 
 JAR=altoslib_$(ALTOSLIB_VERSION).jar
 
index e82931b23499c809d0fe89d4d506779d5514779d..4b3d17fe727d8bcb903c490c026acc09bc9ec235 100644 (file)
@@ -20,8 +20,8 @@ package altosui;
 import java.awt.*;
 import libaltosJNI.*;
 
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class Altos extends AltosUILib {
 
index 10080efd47896b8bfd05a2b52f55b38636951331..453246bcc5c2ad16d918d4138dd12483820a2ced 100644 (file)
@@ -21,8 +21,8 @@ import java.util.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosAscent extends AltosUIFlightTab {
        JLabel  cur, max;
index 68dab227481e0f75d126464f31821e6942f37064..28f26a62c9e4442a8adabfe658fe85aa21a1a861 100644 (file)
@@ -19,8 +19,8 @@ package altosui;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosCompanionInfo extends JTable implements AltosFlightDisplay {
        private AltosFlightInfoTableModel model;
index 3c5415d2a4be95769c7a35ab9c760474334ca4f6..39ed5ca4ca05ea5ea66d63ea1cbd81eda212f43f 100644 (file)
@@ -22,8 +22,8 @@ import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.text.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosConfig implements ActionListener {
 
index 61208dfe91676a81acfdc1b0f317d9e48d53a6a6..50a25f00615b3f86b68e917f95ef7849e97805b0 100644 (file)
 
 package altosui;
 
+import java.text.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosConfigPyroUI
        extends AltosUIDialog
@@ -87,9 +88,9 @@ public class AltosConfigPyroUI
 
                        if (units != null) {
                                try {
-                                       double v = units.parse(value.getText(), !imperial_units);
+                                       double v = units.parse_locale(value.getText(), !imperial_units);
                                        set(enabled(), v);
-                               } catch (NumberFormatException ne) {
+                               } catch (ParseException pe) {
                                        set(enabled(), 0.0);
                                }
                        }
@@ -129,9 +130,9 @@ public class AltosConfigPyroUI
                                AltosUnits units = AltosPyro.pyro_to_units(flag);
                                try {
                                        if (units != null)
-                                               return units.parse(value.getText());
-                                       return Double.parseDouble(value.getText());
-                               } catch (NumberFormatException e) {
+                                               return units.parse_locale(value.getText());
+                                       return AltosParse.parse_double_locale(value.getText());
+                               } catch (ParseException e) {
                                        throw new AltosConfigDataException("\"%s\": %s\n", value.getText(), e.getMessage());
                                }
                        }
@@ -298,8 +299,8 @@ public class AltosConfigPyroUI
                String  v = pyro_firing_time_value.getSelectedItem().toString();
 
                try {
-                       return Double.parseDouble(v);
-               } catch (NumberFormatException e) {
+                       return AltosParse.parse_double_locale(v);
+               } catch (ParseException e) {
                        throw new AltosConfigDataException("Invalid pyro firing time \"%s\"", v);
                }
        }
index cd20a174300c822d70362f541fbdd09d2eed800c..112ebbc6164ded3f0c68861bfbd5d04ebdc4494c 100644 (file)
@@ -21,8 +21,8 @@ import java.awt.event.*;
 import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosConfigTD implements ActionListener {
 
index 52225108fbf135cd5a45b290289a02902bd5e5ca..857ab64d734eda21b98cffec34d8fc14549dd83f 100644 (file)
@@ -21,8 +21,8 @@ import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosConfigTDUI
        extends AltosUIDialog
index 54f06065af4da4df28c43880eb066b8f83a7a0c5..76837b31159d2e0fd31ec12ea59acb581f2de29e 100644 (file)
@@ -21,8 +21,9 @@ import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import java.text.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosConfigUI
        extends AltosUIDialog
@@ -43,6 +44,7 @@ public class AltosConfigUI
        JLabel                  rate_label;
        JLabel                  aprs_interval_label;
        JLabel                  aprs_ssid_label;
+       JLabel                  aprs_format_label;
        JLabel                  flight_log_max_label;
        JLabel                  ignite_mode_label;
        JLabel                  pad_orientation_label;
@@ -66,6 +68,7 @@ public class AltosConfigUI
        AltosUIRateList         rate_value;
        JComboBox<String>       aprs_interval_value;
        JComboBox<Integer>      aprs_ssid_value;
+       JComboBox<String>       aprs_format_value;
        JComboBox<String>       flight_log_max_value;
        JComboBox<String>       ignite_mode_value;
        JComboBox<String>       pad_orientation_value;
@@ -218,11 +221,20 @@ public class AltosConfigUI
 
        void set_aprs_ssid_tool_tip() {
                if (aprs_ssid_value.isEnabled())
-                       aprs_interval_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
-               else if (aprs_interval_value.isEnabled())
-                       aprs_interval_value.setToolTipText("Software version doesn't support setting the APRS SSID");
+                       aprs_ssid_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
+               else if (aprs_ssid_value.isEnabled())
+                       aprs_ssid_value.setToolTipText("Software version doesn't support setting the APRS SSID");
                else
-                       aprs_interval_value.setToolTipText("Hardware doesn't support APRS");
+                       aprs_ssid_value.setToolTipText("Hardware doesn't support APRS");
+       }
+
+       void set_aprs_format_tool_tip() {
+               if (aprs_format_value.isEnabled())
+                       aprs_format_value.setToolTipText("Set the APRS format (compressed/uncompressed)");
+               else if (aprs_format_value.isEnabled())
+                       aprs_format_value.setToolTipText("Software version doesn't support setting the APRS format");
+               else
+                       aprs_format_value.setToolTipText("Hardware doesn't support APRS");
        }
 
        void set_flight_log_max_tool_tip() {
@@ -577,6 +589,33 @@ public class AltosConfigUI
                set_aprs_ssid_tool_tip();
                row++;
 
+               /* APRS format */
+               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;
+               aprs_format_label = new JLabel("APRS format:");
+               pane.add(aprs_format_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;
+               aprs_format_value = new JComboBox<String>(AltosLib.ao_aprs_format_name);
+               aprs_format_value.setEditable(false);
+               aprs_format_value.addItemListener(this);
+               aprs_format_value.setMaximumRowCount(AltosLib.ao_aprs_format_name.length);
+               pane.add(aprs_format_value, c);
+               set_aprs_format_tool_tip();
+               row++;
+
                /* Callsign */
                c = new GridBagConstraints();
                c.gridx = 0; c.gridy = row;
@@ -938,8 +977,13 @@ public class AltosConfigUI
 
        }
 
-       public int main_deploy() {
-               return (int) (AltosConvert.height.parse(main_deploy_value.getSelectedItem().toString()) + 0.5);
+       public int main_deploy() throws AltosConfigDataException {
+               String  str = main_deploy_value.getSelectedItem().toString();
+               try {
+                       return (int) (AltosConvert.height.parse_locale(str) + 0.5);
+               } catch (ParseException pe) {
+                       throw new AltosConfigDataException("invalid main deploy height %s", str);
+               }
        }
 
        String get_main_deploy_label() {
@@ -968,14 +1012,21 @@ public class AltosConfigUI
                String v = main_deploy_value.getSelectedItem().toString();
                main_deploy_label.setText(get_main_deploy_label());
                set_main_deploy_values();
-               int m = (int) (AltosConvert.height.parse(v, !imperial_units) + 0.5);
-               set_main_deploy(m);
+               try {
+                       int m = (int) (AltosConvert.height.parse_locale(v, !imperial_units) + 0.5);
+                       set_main_deploy(m);
+               } catch (ParseException pe) {
+               }
 
                if (tracker_motion_value.isEnabled()) {
                        String motion = tracker_motion_value.getSelectedItem().toString();
                        tracker_motion_label.setText(get_tracker_motion_label());
                        set_tracker_motion_values();
-                       set_tracker_motion((int) (AltosConvert.height.parse(motion, !imperial_units) + 0.5));
+                       try {
+                               int m = (int) (AltosConvert.height.parse_locale(motion, !imperial_units) + 0.5);
+                               set_tracker_motion(m);
+                       } catch (ParseException pe) {
+                       }
                }
 
                if (!was_dirty)
@@ -1234,7 +1285,12 @@ public class AltosConfigUI
        }
 
        public int tracker_motion() throws AltosConfigDataException {
-               return (int) AltosConvert.height.parse(tracker_motion_value.getSelectedItem().toString());
+               String str = tracker_motion_value.getSelectedItem().toString();
+               try {
+                       return (int) (AltosConvert.height.parse_locale(str) + 0.5);
+               } catch (ParseException pe) {
+                       throw new AltosConfigDataException("invalid tracker motion %s", str);
+               }
        }
 
        public void set_tracker_interval(int tracker_interval) {
@@ -1313,4 +1369,16 @@ public class AltosConfigUI
                Integer i = (Integer) aprs_ssid_value.getSelectedItem();
                return i;
        }
+
+       public void set_aprs_format(int new_aprs_format) {
+               aprs_format_value.setVisible(new_aprs_format >= 0);
+               aprs_format_label.setVisible(new_aprs_format >= 0);
+
+               aprs_format_value.setSelectedIndex(Math.max(0,new_aprs_format));
+               set_aprs_format_tool_tip();
+       }
+
+       public int aprs_format() throws AltosConfigDataException {
+               return aprs_format_value.getSelectedIndex();
+       }
 }
index 7bc50570eaca77c329a90dcf372b33e0e6f125ee..fa815762db78008e1adc8428669aba387289184c 100644 (file)
@@ -22,7 +22,7 @@ import java.awt.event.*;
 import java.beans.*;
 import javax.swing.*;
 import javax.swing.event.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosConfigureUI
        extends AltosUIConfigure
index 0db1a4c2f27b8c4405a2a346a75ad32eee3e1032..f5b928169c29e4e4aeee595e7c21aa8c54316217 100644 (file)
@@ -21,8 +21,8 @@ import java.util.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosDescent extends AltosUIFlightTab {
 
index c0d3312b8b71cf4d81c9f15541ce32a2153af55b..815a6fa425d1c459bd0867253f74e21195b47810 100644 (file)
@@ -19,8 +19,8 @@ package altosui;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosFlightStatus extends JComponent implements AltosFlightDisplay {
        GridBagLayout   layout;
index f2031698c377afd2d2df94bc77a13a915ebebb50..b5587a8451534c7dd0853108c45b12674093c552 100644 (file)
@@ -27,7 +27,7 @@ import java.util.*;
 import java.text.*;
 import java.util.prefs.*;
 import java.util.concurrent.LinkedBlockingQueue;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosFlightStatusTableModel extends AbstractTableModel {
        private String[] columnNames = {
index 3f6494b8055655e71fc00814e2be1b3ae0dd2b91..9768c6d117497622cb92d2d554df244a86781559 100644 (file)
@@ -18,7 +18,7 @@
 package altosui;
 
 import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosFlightStatusUpdate implements ActionListener {
 
index 424c57da91db18c36e972044a501610e932c27e4..db33e0d949127b27ec2b39ee4272a463f93b683e 100644 (file)
@@ -22,8 +22,8 @@ import java.awt.event.*;
 import javax.swing.*;
 import java.util.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
        AltosVoice              voice;
@@ -40,7 +40,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
        AltosDescent    descent;
        AltosLanded     landed;
        AltosCompanionInfo      companion;
-       AltosUIMap      sitemap;
+       AltosUIMapNew      sitemap;
        boolean         has_map;
        boolean         has_companion;
        boolean         has_state;
@@ -189,12 +189,12 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
                bag = getContentPane();
                bag.setLayout(new GridBagLayout());
 
-               GridBagConstraints c = new GridBagConstraints();
-
                setTitle(String.format("AltOS %s", reader.name));
 
                /* Stick channel selector at top of table for telemetry monitoring */
                if (serial >= 0) {
+                       set_inset(3);
+
                        // Frequency menu
                        frequencies = new AltosUIFreqList(AltosUIPreferences.frequency(serial));
                        frequencies.set_product("Monitor");
@@ -210,14 +210,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
                                                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);
+                       bag.add (frequencies, constraints(0, 1));
 
                        // Telemetry rate list
                        rates = new AltosUIRateList(AltosUIPreferences.telemetry_rate(serial));
@@ -233,14 +226,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
                                        }
                                });
                        rates.setEnabled(reader.supports_telemetry_rate(AltosLib.ao_telemetry_rate_2400));
-                       c.gridx = 1;
-                       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 (rates, c);
+                       bag.add (rates, constraints(1, 1));
 
                        // Telemetry format list
                        if (reader.supports_telemetry(Altos.ao_telemetry_standard)) {
@@ -252,14 +238,7 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
                                                        reader.save_telemetry();
                                                }
                                        });
-                               c.gridx = 2;
-                               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);
+                               bag.add (telemetries, constraints(2, 1));
                        } else {
                                String  version;
 
@@ -271,26 +250,17 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
                                        version = "Telemetry: None";
 
                                telemetry = new JLabel(version);
-                               c.gridx = 2;
-                               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);
+                               bag.add (telemetry, constraints(2, 1));
                        }
+                       next_row();
                }
+               set_inset(0);
 
                /* Flight status is always visible */
                flightStatus = new AltosFlightStatus();
                displays.add(flightStatus);
-               c.gridx = 0;
-               c.gridy = 1;
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.weightx = 1;
-               c.gridwidth = 3;
-               bag.add(flightStatus, c);
+               bag.add(flightStatus, constraints(0, 4, GridBagConstraints.HORIZONTAL));
+               next_row();
 
                /* The rest of the window uses a tabbed pane to
                 * show one of the alternate data views
@@ -319,17 +289,12 @@ public class AltosFlightUI extends AltosUIFrame implements AltosFlightDisplay {
                has_companion = false;
                has_state = false;
 
-               sitemap = new AltosUIMap();
+               sitemap = new AltosUIMapNew();
                displays.add(sitemap);
                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;
-               bag.add(pane, c);
+               bag.add(pane, constraints(0, 4, GridBagConstraints.BOTH));
 
                setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
 
index f5e339a59a2d26b3968b54d39174a1291c57510f..6e45a092a678b1fc751181ce223135fc0782637c 100644 (file)
@@ -23,8 +23,8 @@ import java.util.ArrayList;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 import org.jfree.chart.ChartPanel;
 import org.jfree.chart.JFreeChart;
@@ -35,7 +35,7 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt
        JTabbedPane             pane;
        AltosGraph              graph;
        AltosUIEnable           enable;
-       AltosUIMap              map;
+       AltosUIMapNew           map;
        AltosState              state;
        AltosGraphDataSet       graphDataSet;
        AltosFlightStats        stats;
@@ -47,7 +47,7 @@ public class AltosGraphUI extends AltosUIFrame implements AltosFontListener, Alt
                for (AltosState state : states) {
                        if (state.gps != null && state.gps.locked && state.gps.nsat >= 4) {
                                if (map == null)
-                                       map = new AltosUIMap();
+                                       map = new AltosUIMapNew();
                                map.show(state, null);
                                has_gps = true;
                        }
index 204d81ed2c26c6d53df13d390e567c97393cd690..d54b05c54f35719743166cfe268f53e95466688a 100644 (file)
@@ -24,8 +24,8 @@ import javax.swing.event.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.Arrays;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDisplay, AltosIdleMonitorListener, DocumentListener {
        AltosDevice             device;
@@ -35,9 +35,11 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
        AltosFlightStatus       flightStatus;
        AltosIgnitor            ignitor;
        AltosIdleMonitor        thread;
+       AltosUIMapNew           sitemap;
        int                     serial;
        boolean                 remote;
        boolean                 has_ignitor;
+       boolean                 has_map;
 
        void stop_display() {
                if (thread != null) {
@@ -83,11 +85,26 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
                                has_ignitor = false;
                        }
                }
+               if (state.gps != null && state.gps.connected) {
+                       if (!has_map) {
+                               pane.add("Site Map", sitemap);
+                               has_map = true;
+                       }
+               } else {
+                       if (has_map) {
+                               pane.remove(sitemap);
+                               has_map = false;
+                       }
+               }
+
 //             try {
                        pad.show(state, listener_state);
                        flightStatus.show(state, listener_state);
                        flightInfo.show(state, listener_state);
-                       ignitor.show(state, listener_state);
+                       if (has_ignitor)
+                               ignitor.show(state, listener_state);
+                       if (has_map)
+                               sitemap.show(state, listener_state);
 //             } catch (Exception e) {
 //                     System.out.print("Show exception " + e);
 //             }
@@ -119,6 +136,7 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
        public void changedUpdate(DocumentEvent e) {
                if (callsign_value != null) {
                        String  callsign = callsign_value.getText();
+                       System.out.printf("callsign set to %s\n", callsign);
                        thread.set_callsign(callsign);
                        AltosUIPreferences.set_callsign(callsign);
                }
@@ -132,30 +150,6 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
                changedUpdate(e);
        }
 
-       int row = 0;
-
-       public GridBagConstraints constraints (int x, int width, int fill) {
-               GridBagConstraints c = new GridBagConstraints();
-               Insets insets = new Insets(4, 4, 4, 4);
-
-               c.insets = insets;
-               c.fill = fill;
-               if (width == 3)
-                       c.anchor = GridBagConstraints.CENTER;
-               else if (x == 2)
-                       c.anchor = GridBagConstraints.EAST;
-               else
-                       c.anchor = GridBagConstraints.WEST;
-               c.gridx = x;
-               c.gridwidth = width;
-               c.gridy = row;
-               return c;
-       }
-
-       public GridBagConstraints constraints(int x, int width) {
-               return constraints(x, width, GridBagConstraints.NONE);
-       }
-
        void idle_exception(JFrame owner, Exception e) {
                if (e instanceof FileNotFoundException) {
                        JOptionPane.showMessageDialog(owner,
@@ -209,11 +203,14 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
                AltosSerial link;
                try {
                        link = new AltosSerial(device);
-                       link.set_frame(this);
                } catch (Exception ex) {
                        idle_exception(in_owner, ex);
                        return;
                }
+               link.set_frame(this);
+
+               /* We let the user set the freq/callsign, so don't bother with the cancel dialog */
+               link.set_cancel_enable(false);
 
                bag = getContentPane();
                bag.setLayout(new GridBagLayout());
@@ -222,6 +219,8 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
 
                /* Stick frequency selector at top of table for telemetry monitoring */
                if (remote && serial >= 0) {
+                       set_inset(3);
+
                        // Frequency menu
                        frequencies = new AltosUIFreqList(AltosUIPreferences.frequency(serial));
                        frequencies.addActionListener(new ActionListener() {
@@ -238,15 +237,17 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
                        callsign_value = new JTextField(AltosUIPreferences.callsign());
                        callsign_value.getDocument().addDocumentListener(this);
                        callsign_value.setToolTipText("Callsign sent in packet mode");
-                       bag.add(callsign_value, constraints(2, 1, GridBagConstraints.BOTH));
-                       row++;
+                       bag.add(callsign_value, constraints(2, 1, GridBagConstraints.HORIZONTAL));
+                       next_row();
                }
 
+               set_inset(0);
 
                /* Flight status is always visible */
                flightStatus = new AltosFlightStatus();
-               bag.add(flightStatus, constraints(0, 3, GridBagConstraints.HORIZONTAL));
-               row++;
+               bag.add(flightStatus, constraints(0, 4, GridBagConstraints.HORIZONTAL));
+
+               next_row();
 
                /* The rest of the window uses a tabbed pane to
                 * show one of the alternate data views
@@ -261,8 +262,10 @@ public class AltosIdleMonitorUI extends AltosUIFrame implements AltosFlightDispl
 
                ignitor = new AltosIgnitor();
 
+               sitemap = new AltosUIMapNew();
+
                /* Make the tabbed pane use the rest of the window space */
-               bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
+               bag.add(pane, constraints(0, 4, GridBagConstraints.BOTH));
 
                setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
 
index 944c659bfd06bf5707fb323ec31eede94c19dbfa..14a2b606cb2651a4db4036645ef857c587f3b569 100644 (file)
@@ -24,8 +24,8 @@ import java.io.*;
 import java.text.*;
 import java.util.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosIgniteUI
        extends AltosUIDialog
@@ -50,8 +50,6 @@ public class AltosIgniteUI
 
        LinkedBlockingQueue<String>     command_queue;
 
-       LinkedBlockingQueue<String>     reply_queue;
-
        class Igniter {
                JRadioButton    button;
                JLabel          status_label;
@@ -150,8 +148,7 @@ public class AltosIgniteUI
                                                }
                                                reply = "status";
                                        } else if (command.equals("get_npyro")) {
-                                               put_reply(String.format("%d", ignite.npyro()));
-                                               continue;
+                                               reply = String.format("npyro %d", ignite.npyro());
                                        } else if (command.equals("quit")) {
                                                ignite.close();
                                                break;
@@ -211,6 +208,9 @@ public class AltosIgniteUI
                        set_ignite_status();
                } else if (reply.equals("fired")) {
                        fired();
+               } else if (reply.startsWith("npyro")) {
+                       npyro = Integer.parseInt(reply.substring(6));
+                       make_ui();
                }
        }
 
@@ -250,24 +250,6 @@ public class AltosIgniteUI
                }
        }
 
-       void put_reply(String reply) {
-               try {
-                       reply_queue.put(reply);
-               } catch (Exception ex) {
-                       ignite_exception(ex);
-               }
-       }
-
-       String get_reply() {
-               String reply = "";
-               try {
-                       reply = reply_queue.take();
-               } catch (Exception ex) {
-                       ignite_exception(ex);
-               }
-               return reply;
-       }
-
        boolean getting_status = false;
 
        boolean visible = false;
@@ -287,12 +269,6 @@ public class AltosIgniteUI
                }
        }
 
-       int get_npyro() {
-               send_command("get_npyro");
-               String reply = get_reply();
-               return Integer.parseInt(reply);
-       }
-
        boolean firing = false;
 
        void start_fire(String which) {
@@ -310,8 +286,9 @@ public class AltosIgniteUI
        void close() {
                if (opened) {
                        send_command("quit");
-                       timer.stop();
                }
+               if (timer != null)
+                       timer.stop();
                setVisible(false);
                dispose();
        }
@@ -383,7 +360,6 @@ public class AltosIgniteUI
 
        private boolean open() {
                command_queue = new LinkedBlockingQueue<String>();
-               reply_queue = new LinkedBlockingQueue<String>();
 
                opened = false;
                device = AltosDeviceUIDialog.show(owner, Altos.product_any);
@@ -403,13 +379,7 @@ public class AltosIgniteUI
                return false;
        }
 
-       public AltosIgniteUI(JFrame in_owner) {
-
-               owner = in_owner;
-
-               if (!open())
-                       return;
-
+       private void make_ui() {
                group = new ButtonGroup();
 
                Container               pane = getContentPane();
@@ -422,8 +392,6 @@ public class AltosIgniteUI
                timer_running = false;
                timer.restart();
 
-               owner = in_owner;
-
                pane.setLayout(new GridBagLayout());
 
                c.fill = GridBagConstraints.NONE;
@@ -443,8 +411,6 @@ public class AltosIgniteUI
 
                y++;
 
-               int npyro = get_npyro();
-
                igniters = new Igniter[2 + npyro];
 
                igniters[0] = new Igniter(this, "Apogee", AltosIgnite.Apogee, y++);
@@ -492,4 +458,14 @@ public class AltosIgniteUI
 
                addWindowListener(new ConfigListener(this));
        }
-}
\ No newline at end of file
+
+       public AltosIgniteUI(JFrame in_owner) {
+
+               owner = in_owner;
+
+               if (!open())
+                       return;
+
+               send_command("get_npyro");
+       }
+}
index 82582a28d3bf70e404a6ae954e7a1892d04966f9..682afc5e0f0fb6d1c363fc442e07c9cda0d58fa7 100644 (file)
@@ -20,8 +20,8 @@ package altosui;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosIgnitor extends AltosUIFlightTab {
 
index 2237df0c129d53afd9d091c884aeb1ed6080b905..c2997b29684dfcdfe12ccdacd9293df8da54d248 100644 (file)
@@ -21,8 +21,8 @@ import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import java.io.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosLanded extends AltosUIFlightTab implements ActionListener {
 
index 37077c42fb8553a95f84f59c64ed9e6c940fa173..6dc1ca023772e318713ef68c434ddc65b28ed2b8 100644 (file)
@@ -20,7 +20,7 @@ package altosui;
 import java.io.*;
 import java.util.concurrent.*;
 import java.awt.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosLaunch {
        AltosDevice     device;
index 4c205d44db7448fe0f63f817fabe5af2add21d6a..132001bffeb306cedcb233d35b7f06f605298829 100644 (file)
@@ -23,7 +23,7 @@ import javax.swing.*;
 import java.io.*;
 import java.text.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 class FireButton extends JButton {
        protected void processMouseEvent(MouseEvent e) {
index 6214fa5a58965c82094d07789131c65f34ae5512..2f87b6818db55e5c86a7efd8ebb65508bd5d2dde 100644 (file)
@@ -18,8 +18,8 @@
 package altosui;
 
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosPad extends AltosUIFlightTab {
 
@@ -103,9 +103,12 @@ public class AltosPad extends AltosUIFlightTab {
 
                public double voltage(AltosState state) { return AltosLib.MISSING; }
 
-               public boolean hide(double v) { return v == AltosLib.MISSING; }
                public double good() { return AltosLib.ao_battery_good; }
 
+               public boolean hide(AltosState state, AltosListenerState listener_state, int i) {
+                       return value(state, listener_state, i) == AltosLib.MISSING;
+               }
+
                public double value(AltosState state, AltosListenerState listener_state, int i) {
                        if (listener_state == null)
                                return AltosLib.MISSING;
@@ -243,12 +246,12 @@ public class AltosPad extends AltosUIFlightTab {
        public AltosPad() {
                int y = 0;
                add(new Battery(this, y++));
+               add(new ReceiverBattery(this, y++));
                add(new Apogee(this, y++));
                add(new Main(this, y++));
                add(new LoggingReady(this, y++));
                add(new GPSLocked(this, y++));
                add(new GPSReady(this, y++));
-               add(new ReceiverBattery(this, y++));
                add(new PadLat(this, y++));
                add(new PadLon(this, y++));
                add(new PadAlt(this, y++));
index 0af09651e304509dce7358d5e92f860f63effb63..f6caa4ef55b66ba727f4a33382deed06d1918dfc 100644 (file)
@@ -22,8 +22,8 @@ import java.awt.event.*;
 import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class AltosUI extends AltosUIFrame {
        public AltosVoice voice = new AltosVoice();
@@ -295,7 +295,7 @@ public class AltosUI extends AltosUIFrame {
        }
 
        void LoadMaps() {
-               new AltosUIMapPreload(AltosUI.this);
+               new AltosUIMapPreloadNew(AltosUI.this);
        }
 
        void LaunchController() {
@@ -587,16 +587,7 @@ public class AltosUI extends AltosUIFrame {
                        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);
-                                               i += 2;
-                                       }
-                               } else if (args[i].equals("--replay"))
+                               else if (args[i].equals("--replay"))
                                        process = process_replay;
                                else if (args[i].equals("--kml"))
                                        process = process_kml;
index 6d4dabfa86ba8338c7514bfcf8032ef145e5d8d4..2f22a4a372c24f58d702a04d0e5c16adbddefebe 100644 (file)
@@ -103,16 +103,17 @@ Section "${REG_NAME} Application"
        File "freetts.jar"
        File "jfreechart.jar"
        File "jcommon.jar"
+       File "../icon/${WIN_APP_EXE}"
 
        File "*.dll"
 
        File "../icon/${WIN_APP_ICON}"
 
-       CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+       CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
 SectionEnd
 
 Section "${REG_NAME} Desktop Shortcut"
-       CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}"  "" "$INSTDIR\${WIN_APP_ICON}"
+       CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}"  "" "$INSTDIR\${WIN_APP_ICON}"
 SectionEnd
 
 Section "Firmware"
@@ -153,7 +154,6 @@ Section "File Associations"
 
        SetOutPath $INSTDIR
 
-       File "../icon/${WIN_APP_EXE}"
        File "../icon/${WIN_TELEM_EXE}"
        File "../icon/${WIN_EEPROM_EXE}"
 
@@ -165,15 +165,13 @@ Section "File Associations"
        DeleteRegKey   HKCR ".telem\${PROG_ID_EEPROM}"
        DeleteRegValue HKCR ".telem\OpenWithProgids" "${PROG_ID_EEPROM}"
 
-       SearchPath $1 "javaw.exe"
-
        ; .eeprom elements
 
        WriteRegStr HKCR "${PROG_ID_EEPROM}"            ""                              "Altus Metrum Log File"
        WriteRegStr HKCR "${PROG_ID_EEPROM}"            "FriendlyTypeName"              "Altus Metrum Log File"
        WriteRegStr HKCR "${PROG_ID_EEPROM}\CurVer"     ""                              "${PROG_ID_EEPROM}"
        WriteRegStr HKCR "${PROG_ID_EEPROM}\DefaultIcon" ""                             '"$INSTDIR\${WIN_EEPROM_EXE}",-101'
-  WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" ""                     '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+  WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" ""                     '"$INSTDIR\${WIN_APP_EXE}" "%1"'
 
        WriteRegStr HKCR ".eeprom"                      ""                              "${PROG_ID_EEPROM}"
        WriteRegStr HKCR ".eeprom"                      "PerceivedType"                 "Altus Metrum Log File"
@@ -188,7 +186,7 @@ Section "File Associations"
        WriteRegStr HKCR "${PROG_ID_TELEM}"             "FriendlyTypeName"              "Altus Metrum Telemetry File"
        WriteRegStr HKCR "${PROG_ID_TELEM}\CurVer"      ""                              "${PROG_ID_TELEM}"
        WriteRegStr HKCR "${PROG_ID_TELEM}\DefaultIcon" ""                              '"$INSTDIR\${WIN_TELEM_EXE}",-101'
-  WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" ""                      '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+  WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" ""                      '"$INSTDIR\${WIN_APP_EXE}" "%1"'
 
        WriteRegStr HKCR ".telem"                       ""                              "${PROG_ID_TELEM}"
        WriteRegStr HKCR ".telem"                       "PerceivedType"                 "Altus Metrum Telemetry File"
index 4ad8a77a3a82014347bac0626df16d61a39ef165..943408ec484fad54de65bf819182e6be83eeec31 100644 (file)
@@ -1,4 +1,3 @@
-AltosUIVersion.java
 bin
 classaltosuilib.stamp
 altosuilib*.jar
index 0dd1cab231b3ed32db9fe0211591115a4d6cf4dc..0d7d07c04a570284a8ed89cfb46ecd3cf6896369 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosBTDevice extends altos_bt_device implements AltosDevice {
 
@@ -104,17 +104,20 @@ public class AltosBTDevice extends altos_bt_device implements AltosDevice {
                return false;
        }
 
+       public int hashCode() {
+               return getName().hashCode() ^ getAddr().hashCode();
+       }
+
        public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+
                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) {
                AltosUILib.load_library();
                libaltos.altos_bt_fill_in(name, addr,this);
index eebad36b5865eebf17c2b96d3d47d5449acec1f5..ca6c436e7db5a449ba80a5cb2e8c8525f56ba109 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.util.*;
 import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosBTDeviceIterator implements Iterator<AltosBTDevice> {
        AltosBTDevice   current;
index 73ee473fa6e833a1c1dbfbf2abdd770e934bfc9b..0d10fafd991cdf856a461c1884395c199a7b2dc9 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosBTKnown implements Iterable<AltosBTDevice> {
        LinkedList<AltosBTDevice>       devices = new LinkedList<AltosBTDevice>();
index c4ac363f5267974e6c838666cad81b664be60e10..8a912c946068905bd2a049a05d2a420f8fd50129 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
@@ -23,7 +23,7 @@ import javax.swing.*;
 import javax.swing.plaf.basic.*;
 import java.util.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosBTManage extends AltosUIDialog implements ActionListener, Iterable<AltosBTDevice> {
        LinkedBlockingQueue<AltosBTDevice> found_devices;
index 94d523fe20b3e433704a9cee1672ec7e69293d95..281a9081091aaeefe2880f077e95fa25a8d6a66d 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import java.io.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosCSVUI
        extends AltosUIDialog
index 6253e3e4c254f1e51e73a0a8d437c7b281642576..04d93f6e59a8a5ea89e63e78b54c5e51914913d2 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
+import java.text.*;
 import java.awt.event.*;
 import javax.swing.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 class AltosEditFreqUI extends AltosUIDialog implements ActionListener {
        Frame           frame;
@@ -51,10 +52,10 @@ class AltosEditFreqUI extends AltosUIDialog implements ActionListener {
                String  d_s = description.getText();
 
                try {
-                       double  f_d = Double.parseDouble(f_s);
+                       double  f_d = AltosParse.parse_double_locale(f_s);
 
                        return new AltosFrequency(f_d, d_s);
-               } catch (NumberFormatException ne) {
+               } catch (ParseException ne) {
                }
                return null;
        }
index 3fe76687e8df39e4c49c1f61efe71a03d79ad04e..d30aec3ffce9c5ee217c0a9616ee01b7a5afeb39 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.*;
 import javax.swing.filechooser.FileNameExtensionFilter;
 import java.io.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosDataChooser extends JFileChooser {
        JFrame  frame;
index 401387a46b9e6afa0f8853a55d5e48d482fca975..64a99596a6a205d7b3844fc01f6be43aa49bc024 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import libaltosJNI.*;
 
index 5fb1331ee210a5cf40618e5b58a7e91c69a0974f..a3c5d2835745c583ec6e7ffd93fb254cb008b483 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.*;
 import java.awt.*;
index 9618895ee04acf12f20a25aa8edc18007497c97d..aa6d323f345a650127453e7007cd1c8a6b65ab6b 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.*;
 import java.awt.*;
index ccf8b3ef22fb90329cdbd4b654a3be95cd65c37d..73d85698ca98f589373d894a2f7f305f9b1305d7 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import javax.swing.*;
 import java.io.*;
 import java.text.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosDisplayThread extends Thread {
 
index 9fb21cf443f2b76a0d22275c6b1796e2ba4803a9..7648382d84fb08e04f978ec35ca95544077a24bd 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.event.*;
 import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosEepromDelete implements Runnable {
        AltosEepromList         flights;
index 47a62ef7e97b1aaa029fd4680ba24ed7f7ad09fa..d97edd12ec9059041d98a0a631ac682a2b7ba1c9 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.event.*;
 import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosEepromManage implements ActionListener {
 
index 83c5c1d0c73c48dbdc3f321a665c67906901188f..28fa734df33a060a5cb55e60b8d5601968cf19a6 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
index ac39fb1dedd443886583d5ab99cdacebd45edcbe..5f0df7d3ec1b4e3eab77c5ab8886b78bc717819b 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosEepromMonitorUI extends AltosUIDialog implements AltosEepromMonitor {
        JFrame          owner;
index b291c35a08197d75b68c0f7c7dbff8fe7197352c..56b10ef0b09a7c8efd41ce375dfae80c2be9cd20 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.*;
 import javax.swing.border.*;
 import java.awt.*;
 import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 class AltosEepromItem implements ActionListener {
        AltosEepromLog  log;
index 6e497c429be6bb9cbdc93674e29537e13c3b7e03..0ab085cf1279d776b7eb16277ef070eca7d8c5ea 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
@@ -23,7 +23,7 @@ import javax.swing.*;
 import javax.swing.filechooser.FileNameExtensionFilter;
 import java.io.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosFlashUI
        extends AltosUIDialog
@@ -54,7 +54,7 @@ public class AltosFlashUI
        // Flash controller
        AltosProgrammer programmer;
 
-       private static String[] pair_programmed = {
+       private static final String[] pair_programmed_files = {
                "teleballoon",
                "telebt-v1",
                "teledongle-v0",
@@ -67,20 +67,34 @@ public class AltosFlashUI
                "teleterra"
        };
 
+       private static final String[] pair_programmed_devices = {
+               "TeleBalloon",
+               "TeleBT-v1",
+               "TeleDongle-v0",
+               "TeleFire",
+               "TeleMetrum-v0",
+               "TeleMetrum-v1",
+               "TeleMini",
+               "TeleNano",
+               "TeleShield",
+               "TeleTerra"
+       };
+
        private boolean is_pair_programmed() {
 
                if (file != null) {
                        String  name = file.getName();
-                       for (int i = 0; i < pair_programmed.length; i++) {
-                               if (name.startsWith(pair_programmed[i]))
+                       for (int i = 0; i < pair_programmed_files.length; i++) {
+                               if (name.startsWith(pair_programmed_files[i]))
                                        return true;
                        }
                }
                if (device != null) {
-                       if (!device.matchProduct(AltosLib.product_altusmetrum) &&
-                           (device.matchProduct(AltosLib.product_teledongle) ||
-                            device.matchProduct(AltosLib.product_telebt)))
-                               return true;
+                       String  name = device.toString();
+                       for (int i = 0; i < pair_programmed_devices.length; i++) {
+                               if (name.startsWith(pair_programmed_devices[i]))
+                                       return true;
+                       }
                }
                return false;
        }
diff --git a/altosuilib/AltosFlightDisplay.java b/altosuilib/AltosFlightDisplay.java
deleted file mode 100644 (file)
index ac65c49..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright © 2010 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- */
-
-package org.altusmetrum.altosuilib_6;
-
-import org.altusmetrum.altoslib_6.*;
-
-public interface AltosFlightDisplay extends AltosUnitsListener, AltosFontListener {
-       void reset();
-
-       void show(AltosState state, AltosListenerState listener_state);
-
-       String getName();
-}
index f9d7d180bcf00df1544959862d94e914d7361344..e55004e41593dde7624ff26763a00c813da2b3ff 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.table.*;
 
index 8a686646f615fede8d74d01dcede37a7be34fec6..9c613365af69105c1866558095a592b84451ebad 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import javax.swing.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosFlightStatsTable extends JComponent implements AltosFontListener {
        GridBagLayout   layout;
diff --git a/altosuilib/AltosFontListener.java b/altosuilib/AltosFontListener.java
deleted file mode 100644 (file)
index 9362527..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * 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.altosuilib_6;
-
-public interface AltosFontListener {
-       void font_size_changed(int font_size);
-}
index d7739228585997b8cfe11aab5230e438b5949c77..be013d579168d0ec6e9ffa36d9249af14aa9eb78 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.ArrayList;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
index 47989d0e9549d10b3e2490a3d81e4599c9992d40..30d436ae2dd1b6fb6674321cff2d6b39467ba221 100644 (file)
@@ -15,9 +15,9 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosGraphDataPoint implements AltosUIDataPoint {
 
index b9c9d2a8855c65b32fe88adc6c5fd24bc28e34d6..7902f40718448378e86b1dba96a832748eaf6010 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.lang.*;
 import java.io.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 class AltosGraphIterator implements Iterator<AltosUIDataPoint> {
        AltosGraphDataSet       dataSet;
index 89a656c9004d1af67d59abb6c7973365a2136600..d87c38e22c2ffeb6bcb149afb70120523d3ba892 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.table.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosInfoTable extends JTable implements AltosFlightDisplay, HierarchyListener {
        private AltosFlightInfoTableModel model;
@@ -137,6 +137,8 @@ public class AltosInfoTable extends JTable implements AltosFlightDisplay, Hierar
                if (state != null) {
                        if (state.device_type != AltosLib.MISSING)
                                info_add_row(0, "Device", "%s", AltosLib.product_name(state.device_type));
+                       else if (state.product != null)
+                               info_add_row(0, "Device", "%s", state.product);
                        if (state.altitude() != AltosLib.MISSING)
                                info_add_row(0, "Altitude", "%6.0f    m", state.altitude());
                        if (state.ground_altitude() != AltosLib.MISSING)
index fa33c4b678d1a30d81dd910b872c44999bdfbaa2..b47aa49388c5b8dbd386c99c5a69245cf0d501b6 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.*;
 
index 05d06ac46ec7421f0e2dc6a2eb3e5f63a1e52557..8cbd1d10f81b4bad2abfea4526ba4ad2073b072f 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import javax.swing.*;
index 1274a64a237092304e103d4e3d54f21055508d8b..0c900787304ad50e05fdb406b3b566fc9f216744 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 public interface AltosPositionListener {
        public void position_changed(int position);
index 99e4d00428273b364f272f043b8546f7e3a6472f..257674dcf10288ea15b30236cc6b1ab14509f33d 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosRomconfigUI
        extends AltosUIDialog
index 5a7e21b18867ea02c6e1b3e64033b5e77bed077f..e2efd4b348049a0367b603b2b296f776d52c18e5 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
@@ -25,7 +25,7 @@ import java.io.*;
 import java.util.*;
 import java.text.*;
 import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 class AltosScanResult {
        String          callsign;
@@ -62,9 +62,18 @@ class AltosScanResult {
                rate = in_rate;
        }
 
-       public boolean equals(AltosScanResult other) {
+       public int hashCode() {
+               return serial ^ frequency.hashCode() ^ telemetry ^ rate;
+       }
+
+       public boolean equals(Object o) {
+               if (o == null)
+                       return false;
+               if (!(o instanceof AltosScanResult))
+                       return false;
+               AltosScanResult other = (AltosScanResult) o;
                return (serial == other.serial &&
-                       frequency.frequency == other.frequency.frequency &&
+                       frequency.equals(other.frequency) &&
                        telemetry == other.telemetry &&
                        rate == other.rate);
        }
index 95815a7b28553e40e29f67962a909a8bcb934d50..ff8d900abf35a2499c1eb4c957bb7adeb9760865 100644 (file)
  * Deal with TeleDongle on a serial port
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.*;
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 import libaltosJNI.*;
 
 /*
index 0487e14677b078cfcefa06d3254e297ce63eafa2..2b198aec0c52a43f30ef05e4a23b9aac13c5aef4 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 public class AltosSerialInUseException extends Exception {
        public AltosDevice      device;
index 89f5493bd38910d55126262291390f82537cd124..155e7ed6d4b44d349f3166f00707582e6129f9f7 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.ArrayList;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
index b022aeec8c40efbfeeae955dd484d182ff942b3c..146acda8ad139d9452d376e1f7b5270f0404ebe9 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
 import java.beans.*;
 import javax.swing.*;
 import javax.swing.event.*;
+import org.altusmetrum.altoslib_8.*;
 
 class DelegatingRenderer implements ListCellRenderer<Object> {
 
@@ -269,8 +270,8 @@ public class AltosUIConfigure
                row++;
 
                pane.add(new JLabel (String.format("AltOS version %s (%smaps key)",
-                                                  AltosUIVersion.version,
-                                                  AltosUIVersion.has_google_maps_api_key() ? "" : "no ")),
+                                                  AltosVersion.version,
+                                                  AltosVersion.has_google_maps_api_key() ? "" : "no ")),
                         constraints(0, 3));
                row++;
 
index a0e41fef947bf5a74367915f0dcfed0987aa81d7..39b8f900933d32ddc8dafc6c392bcf69125a1fdd 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 public class AltosUIDataMissing extends Exception {
        public int      id;
index 82ce862faf1cd902aec88713a9674037d20b0972..793fabfb3feff73888e94a93680bb3ca2d99500d 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 public interface AltosUIDataPoint {
        public abstract double x() throws AltosUIDataMissing;
index 6293911d0a4393780943ab2bf0a704d1157bddd8..b74720009c01031a383f9eade3ae37272aba46a2 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 public interface AltosUIDataSet {
        public abstract String name();
index 77e549c4457ec137c04a8e0d0580605f2aa2424f..cfbf2cd3d80910e5c2b7cf3f058b06e3ed07fe12 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
index 481e5b87240d46941fd6fa2d0ec1045233303a0e..b4d8d81e0631f8248785ef62f42e862e79a55b1a 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
@@ -23,7 +23,7 @@ import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
index ea4f0cb0608487a6bfe852e824376488d96de123..a2a3e367d5fe8b61cb7820c736564fbb664c232c 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.util.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public abstract class AltosUIFlightTab extends JComponent implements AltosFlightDisplay, HierarchyListener {
        public GridBagLayout    layout;
index 39b1eb7348aa1d0ba1cc321720eb4d300d6a03da..7901510344086007001d7350bf66b19c37df0dde 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
@@ -207,6 +207,62 @@ public class AltosUIFrame extends JFrame implements AltosUIListener, AltosPositi
                        }
                }
        }
+
+       int row = 0;
+
+       public void next_row() {
+               row++;
+       }
+
+       int inset = 0;
+
+       public void set_inset(int i) {
+               inset = i;
+       }
+
+       public GridBagConstraints constraints (int x, int width, int fill, int anchor, double weightx, double weighty) {
+               return new GridBagConstraints(x,                        /* x */
+                                             row,                      /* y */
+                                             width,                    /* width */
+                                             1,                        /* height */
+                                             weightx,                  /* weightx */
+                                             weighty,                  /* weighty */
+                                             anchor,                   /* anchor */
+                                             fill,                     /* fill */
+                                             new Insets(inset,inset,inset,inset),      /* insets */
+                                             0,                        /* ipadx */
+                                             0);                       /* ipady */
+       }
+
+       public GridBagConstraints constraints (int x, int width, int fill, int anchor) {
+               double  weightx = 0;
+               double  weighty = 0;
+
+               if (fill == GridBagConstraints.NONE) {
+                       weightx = 0;
+                       weighty = 0;
+               } else if (fill == GridBagConstraints.HORIZONTAL) {
+                       weightx = 1;
+                       weighty = 0;
+               } else if (fill == GridBagConstraints.VERTICAL) {
+                       weightx = 0;
+                       weighty = 1;
+               } else if (fill == GridBagConstraints.BOTH) {
+                       weightx = 1;
+                       weighty = 1;
+               }
+
+               return constraints (x, width, fill, anchor, weightx, weighty);
+       }
+
+       public GridBagConstraints constraints (int x, int width, int fill) {
+               return constraints (x, width, fill, GridBagConstraints.WEST);
+       }
+
+       public GridBagConstraints constraints(int x, int width) {
+               return constraints(x, width, GridBagConstraints.NONE);
+       }
+
        void init() {
                AltosUIPreferences.register_ui_listener(this);
                AltosUIPreferences.register_position_listener(this);
index 430069f57874f3f9fe76e09820ff3eb6b1214a8f..9479087e8be371d59196c633135da9cde7410c8d 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosUIFreqList extends JComboBox<AltosFrequency> {
 
index d20aa54b8a9311455e3d6781e8ce15a17a8ecd80..a11978d07e2b2e98d22376fa982c1328d258d883 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.ArrayList;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
index fcd3546f89896a6ab2b6b5ed3c90a5300c7d806e..0fe685ef89a7d940e8f15252ab9d5805ba0b78a4 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.ArrayList;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
diff --git a/altosuilib/AltosUIImage.java b/altosuilib/AltosUIImage.java
new file mode 100644 (file)
index 0000000..7774374
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_8;
+
+import javax.swing.*;
+import javax.imageio.ImageIO;
+import java.awt.image.*;
+import java.awt.*;
+import java.io.*;
+import java.net.*;
+
+public class AltosUIImage implements AltosImage {
+       public Image    image;
+
+       /* Discard storage for image */
+       public void flush() {
+               image.flush();
+       }
+
+       public AltosUIImage(Image image) {
+               this.image = image;
+       }
+}
index f2e772184d07045f0ba9e50a002ab6ea56b6c803..0a940d9787415a29a5367511d6857ba468e873f0 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public abstract class AltosUIIndicator implements AltosFontListener, AltosUnitsListener {
        JLabel          label;
diff --git a/altosuilib/AltosUILatLon.java b/altosuilib/AltosUILatLon.java
deleted file mode 100644 (file)
index 72ff74d..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUILatLon {
-       public double   lat;
-       public double   lon;
-
-       public boolean equals(AltosUILatLon other) {
-               if (other == null)
-                       return false;
-               return lat == other.lat && lon == other.lon;
-       }
-
-       public AltosUILatLon(double lat, double lon) {
-               this.lat = lat;
-               this.lon = lon;
-       }
-}
index 2fa6cbd621fc16df3ceef1a65f3079fb681a28c9..2fc203177eb7fcd7a0c79fe68cb5fde1deaeab90 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import libaltosJNI.*;
 
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosUILib extends AltosLib {
 
index 9eec0b24b5e9595e9fd711270a119d2d02301e1b..5b49e7da9ddcfe95a87a46eb07787dd0115e0647 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 public interface AltosUIListener {
        public void ui_changed(String look_and_feel);
diff --git a/altosuilib/AltosUIMap.java b/altosuilib/AltosUIMap.java
deleted file mode 100644 (file)
index 5c6d5bd..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * 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 org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMap extends JComponent implements AltosFlightDisplay, AltosUIMapZoomListener {
-
-       static final int px_size = 512;
-
-       static final int maptype_hybrid = 0;
-       static final int maptype_roadmap = 1;
-       static final int maptype_satellite = 2;
-       static final int maptype_terrain = 3;
-       static final int maptype_default = maptype_hybrid;
-
-       static final String[] maptype_names = {
-               "hybrid",
-               "roadmap",
-               "satellite",
-               "terrain"
-       };
-
-       public static final String[] maptype_labels = {
-               "Hybrid",
-               "Roadmap",
-               "Satellite",
-               "Terrain"
-       };
-
-       public static final 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
-               Color.BLACK,  // invalid
-               Color.CYAN,   // stateless
-       };
-
-       public void reset() {
-               // nothing
-       }
-
-       public void font_size_changed(int font_size) {
-               view.set_font();
-       }
-
-       public void units_changed(boolean imperial_units) {
-               view.set_units();
-       }
-
-       JLabel  zoom_label;
-
-       private void set_zoom_label() {
-               zoom_label.setText(String.format("Zoom %d", view.zoom() - view.default_zoom));
-       }
-
-       public void zoom_changed(int zoom) {
-               set_zoom_label();
-       }
-
-       public void set_zoom(int zoom) {
-               view.set_zoom(zoom);
-       }
-
-       public int get_zoom() {
-               return view.zoom();
-       }
-
-       public void set_maptype(int type) {
-               view.set_maptype(type);
-               maptype_combo.setSelectedIndex(type);
-       }
-
-       public void show(AltosState state, AltosListenerState listener_state) {
-               view.show(state, listener_state);
-       }
-
-       public void centre(double lat, double lon) {
-               view.centre(lat, lon);
-       }
-
-       public void centre(AltosState state) {
-               if (!state.gps.locked && state.gps.nsat < 4)
-                       return;
-               centre(state.gps.lat, state.gps.lon);
-       }
-
-       public void add_mark(double lat, double lon, int state) {
-               view.add_mark(lat, lon, state);
-       }
-
-       public void clear_marks() {
-               view.clear_marks();
-       }
-
-       AltosUIMapView  view;
-
-       private GridBagLayout layout = new GridBagLayout();
-
-       JComboBox<String>       maptype_combo;
-
-       public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) {
-               view.set_load_params(lat, lon, radius, listener);
-       }
-
-       public boolean all_fetched() {
-               return view.all_fetched();
-       }
-
-       public static void prefetch_maps(double lat, double lon) {
-       }
-
-       public String getName() {
-               return "Map";
-       }
-
-       public AltosUIMap() {
-
-               view = new AltosUIMapView();
-
-               view.setPreferredSize(new Dimension(500,500));
-               view.setVisible(true);
-               view.setEnabled(true);
-               view.add_zoom_listener(this);
-
-               GridBagLayout   my_layout = new GridBagLayout();
-
-               setLayout(my_layout);
-
-               GridBagConstraints c = new GridBagConstraints();
-               c.anchor = GridBagConstraints.CENTER;
-               c.fill = GridBagConstraints.BOTH;
-               c.gridx = 0;
-               c.gridy = 0;
-               c.gridwidth = 1;
-               c.gridheight = 10;
-               c.weightx = 1;
-               c.weighty = 1;
-               add(view, c);
-
-               int     y = 0;
-
-               zoom_label = new JLabel("", JLabel.CENTER);
-               set_zoom_label();
-
-               c = new GridBagConstraints();
-               c.anchor = GridBagConstraints.CENTER;
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.gridx = 1;
-               c.gridy = y++;
-               c.weightx = 0;
-               c.weighty = 0;
-               add(zoom_label, c);
-
-               JButton zoom_reset = new JButton("0");
-               zoom_reset.addActionListener(new ActionListener() {
-                               public void actionPerformed(ActionEvent e) {
-                                       set_zoom(view.default_zoom);
-                               }
-                       });
-
-               c = new GridBagConstraints();
-               c.anchor = GridBagConstraints.CENTER;
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.gridx = 1;
-               c.gridy = y++;
-               c.weightx = 0;
-               c.weighty = 0;
-               add(zoom_reset, c);
-
-               JButton zoom_in = new JButton("+");
-               zoom_in.addActionListener(new ActionListener() {
-                               public void actionPerformed(ActionEvent e) {
-                                       set_zoom(get_zoom() + 1);
-                               }
-                       });
-
-               c = new GridBagConstraints();
-               c.anchor = GridBagConstraints.CENTER;
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.gridx = 1;
-               c.gridy = y++;
-               c.weightx = 0;
-               c.weighty = 0;
-               add(zoom_in, c);
-
-               JButton zoom_out = new JButton("-");
-               zoom_out.addActionListener(new ActionListener() {
-                               public void actionPerformed(ActionEvent e) {
-                                       set_zoom(get_zoom() - 1);
-                               }
-                       });
-               c = new GridBagConstraints();
-               c.anchor = GridBagConstraints.CENTER;
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.gridx = 1;
-               c.gridy = y++;
-               c.weightx = 0;
-               c.weighty = 0;
-               add(zoom_out, c);
-
-               maptype_combo = new JComboBox<String>(maptype_labels);
-
-               maptype_combo.setEditable(false);
-               maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
-               maptype_combo.addItemListener(new ItemListener() {
-                               public void itemStateChanged(ItemEvent e) {
-                                       view.set_maptype(maptype_combo.getSelectedIndex());
-                               }
-                       });
-
-               c = new GridBagConstraints();
-               c.anchor = GridBagConstraints.CENTER;
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.gridx = 1;
-               c.gridy = y++;
-               c.weightx = 0;
-               c.weighty = 0;
-               add(maptype_combo, c);
-       }
-}
diff --git a/altosuilib/AltosUIMapCache.java b/altosuilib/AltosUIMapCache.java
deleted file mode 100644 (file)
index 73401e3..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * 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 org.altusmetrum.altosuilib_6;
-
-import javax.swing.*;
-import javax.imageio.ImageIO;
-import java.awt.image.*;
-import java.awt.*;
-import java.io.*;
-import java.net.*;
-
-public class AltosUIMapCache implements AltosUIMapCacheListener {
-       static final int        success = 0;
-       static final int        loading = 1;
-       static final int        failed = 2;
-       static final int        bad_request = 3;
-       static final int        forbidden = 4;
-
-       int                     min_cache_size;         /* configured minimum cache size */
-       int                     cache_size;             /* current cache size */
-       int                     requested_cache_size;   /* cache size computed by application */
-
-       private Object          fetch_lock = new Object();
-       private Object          cache_lock = new Object();
-
-       AltosUIMapImage[]       images = new AltosUIMapImage[cache_size];
-
-       long                    used;
-
-       public void set_cache_size(int new_size) {
-
-               requested_cache_size = new_size;
-
-               if (new_size < min_cache_size)
-                       new_size = min_cache_size;
-
-               if (new_size == cache_size)
-                       return;
-
-               synchronized(cache_lock) {
-                       AltosUIMapImage[]       new_images = new AltosUIMapImage[new_size];
-
-                       for (int i = 0; i < cache_size; i++) {
-                               if (i < new_size)
-                                       new_images[i] = images[i];
-                               else if (images[i] != null)
-                                       images[i].flush();
-                       }
-                       images = new_images;
-                       cache_size = new_size;
-               }
-       }
-
-       public Image get(AltosUIMapTile tile, AltosUIMapStore store, int width, int height) {
-               int             oldest = -1;
-               long            age = used;
-
-               synchronized(cache_lock) {
-                       AltosUIMapImage image = null;
-                       for (int i = 0; i < cache_size; i++) {
-                               image = images[i];
-
-                               if (image == null) {
-                                       oldest = i;
-                                       break;
-                               }
-                               if (store.equals(image.store)) {
-                                       image.used = used++;
-                                       return image.image;
-                               }
-                               if (image.used < age) {
-                                       oldest = i;
-                                       age = image.used;
-                               }
-                       }
-
-                       try {
-                               image = new AltosUIMapImage(tile, store);
-                               image.used = used++;
-                               if (images[oldest] != null)
-                                       images[oldest].flush();
-
-                               images[oldest] = image;
-
-                               if (image.image == null)
-                                       tile.set_status(loading);
-                               else
-                                       tile.set_status(success);
-
-                               return image.image;
-                       } catch (IOException e) {
-                               tile.set_status(failed);
-                               return null;
-                       }
-               }
-       }
-
-       public void map_cache_changed(int map_cache) {
-               min_cache_size = map_cache;
-
-               set_cache_size(requested_cache_size);
-       }
-
-       public void dispose() {
-               AltosUIPreferences.unregister_map_cache_listener(this);
-
-               for (int i = 0; i < cache_size; i++) {
-                       AltosUIMapImage image = images[i];
-
-                       if (image != null)
-                           image.flush();
-               }
-       }
-
-       public AltosUIMapCache() {
-               min_cache_size = AltosUIPreferences.map_cache();
-
-               set_cache_size(0);
-
-               AltosUIPreferences.register_map_cache_listener(this);
-       }
-}
diff --git a/altosuilib/AltosUIMapCacheListener.java b/altosuilib/AltosUIMapCacheListener.java
deleted file mode 100644 (file)
index 1eec7b0..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-public interface AltosUIMapCacheListener {
-       public void map_cache_changed(int map_cache);
-}
diff --git a/altosuilib/AltosUIMapImage.java b/altosuilib/AltosUIMapImage.java
deleted file mode 100644 (file)
index 97220a4..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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 org.altusmetrum.altosuilib_6;
-
-import javax.swing.*;
-import javax.imageio.ImageIO;
-import java.awt.image.*;
-import java.awt.*;
-import java.io.*;
-import java.net.*;
-
-public class AltosUIMapImage implements AltosUIMapStoreListener {
-       static final long google_maps_ratelimit_ms = 1200;
-       // Google limits static map queries to 50 per minute per IP, so
-       // each query should take at least 1.2 seconds.
-
-       static final int        success = 0;
-       static final int        loading = 1;
-       static final int        failed = 2;
-       static final int        bad_request = 3;
-       static final int        forbidden = 4;
-
-       static long             forbidden_time;
-       static boolean          forbidden_set = false;
-       static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
-
-       AltosUIMapTile          tile;           /* Notify when image has been loaded */
-       Image                   image;
-       AltosUIMapStore         store;
-       long                    used;
-
-       class loader implements Runnable {
-               public void run() {
-                       if (image != null)
-                               tile.notify_image(image);
-                       try {
-                               image = ImageIO.read(store.file);
-                       } catch (Exception ex) {
-                       }
-                       if (image == null)
-                               tile.set_status(failed);
-                       else
-                               tile.set_status(success);
-                       tile.notify_image(image);
-               }
-       }
-
-       private void load() {
-               loader  l = new loader();
-               Thread  lt = new Thread(l);
-               lt.start();
-       }
-
-       public void flush() {
-               if (image != null) {
-                       image.flush();
-                       image = null;
-               }
-       }
-
-       public boolean has_map() {
-               return store.status() == AltosUIMapStore.success;
-       }
-
-       public synchronized void notify_store(AltosUIMapStore store, int status) {
-               switch (status) {
-               case AltosUIMapStore.loading:
-                       break;
-               case AltosUIMapStore.success:
-                       load();
-                       break;
-               default:
-                       tile.set_status(status);
-                       tile.notify_image(null);
-               }
-       }
-
-       public AltosUIMapImage(AltosUIMapTile tile, AltosUIMapStore store) throws IOException {
-               this.tile = tile;
-               this.image = null;
-               this.store = store;
-               this.used = 0;
-
-               int status = store.status();
-               switch (status) {
-               case AltosUIMapStore.loading:
-                       store.add_listener(this);
-                       break;
-               case AltosUIMapStore.success:
-                       load();
-                       break;
-               default:
-                       tile.set_status(status);
-                       tile.notify_image(null);
-                       break;
-               }
-       }
-}
diff --git a/altosuilib/AltosUIMapLine.java b/altosuilib/AltosUIMapLine.java
deleted file mode 100644 (file)
index 2634f84..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapLine {
-       AltosUILatLon   start, end;
-
-       private Font    font = null;
-       static public int stroke_width = 6;
-
-       public void set_font(Font font) {
-               this.font = font;
-       }
-
-       private AltosUILatLon lat_lon(MouseEvent e, AltosUIMapTransform t) {
-               return t.screen_lat_lon(e.getPoint());
-       }
-
-       public void dragged(MouseEvent e, AltosUIMapTransform t) {
-               end = lat_lon(e, t);
-       }
-
-       public void pressed(MouseEvent e, AltosUIMapTransform t) {
-               start = lat_lon(e, t);
-               end = null;
-       }
-
-       private String line_dist() {
-               String  format;
-               AltosGreatCircle        g = new AltosGreatCircle(start.lat, start.lon,
-                                                                end.lat, end.lon);
-               double  distance = g.distance;
-
-               if (AltosConvert.imperial_units) {
-                       distance = AltosConvert.meters_to_feet(distance);
-                       if (distance < 10000) {
-                               format = "%4.0fft";
-                       } else {
-                               distance /= 5280;
-                               if (distance < 10)
-                                       format = "%5.3fmi";
-                               else if (distance < 100)
-                                       format = "%5.2fmi";
-                               else if (distance < 1000)
-                                       format = "%5.1fmi";
-                               else
-                                       format = "%5.0fmi";
-                       }
-               } else {
-                       if (distance < 10000) {
-                               format = "%4.0fm";
-                       } else {
-                               distance /= 1000;
-                               if (distance < 100)
-                                       format = "%5.2fkm";
-                               else if (distance < 1000)
-                                       format = "%5.1fkm";
-                               else
-                                       format = "%5.0fkm";
-                       }
-               }
-               return String.format(format, distance);
-       }
-
-       public void paint(Graphics2D g, AltosUIMapTransform t) {
-
-               if (start == null || end == null)
-                       return;
-
-               g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-
-               Line2D.Double line = new Line2D.Double(t.screen(start),
-                                                      t.screen(end));
-
-               g.setColor(Color.WHITE);
-               g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-               g.draw(line);
-
-               g.setColor(Color.BLUE);
-               g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-               g.draw(line);
-
-               String  message = line_dist();
-               Rectangle2D     bounds;
-               bounds = font.getStringBounds(message, g.getFontRenderContext());
-
-               float x = (float) line.x1;
-               float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
-
-               if (line.x1 < line.x2) {
-                       x -= (float) bounds.getWidth() + 2.0f;
-               } else {
-                       x += 2.0f;
-               }
-
-               g.setFont(font);
-               g.setColor(Color.WHITE);
-               for (int dy = -2; dy <= 2; dy += 2)
-                       for (int dx = -2; dx <= 2; dx += 2)
-                               g.drawString(message, x + dx, y + dy);
-               g.setColor(Color.BLUE);
-               g.drawString(message, x, y);
-       }
-}
diff --git a/altosuilib/AltosUIMapMark.java b/altosuilib/AltosUIMapMark.java
deleted file mode 100644 (file)
index b4b98ef..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapMark {
-
-       AltosUILatLon   lat_lon;
-       int             state;
-
-       static public int stroke_width = 6;
-
-       public void paint(Graphics2D g, AltosUIMapTransform t) {
-
-               Point2D.Double pt = t.screen(lat_lon);
-
-               g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                                  RenderingHints.VALUE_ANTIALIAS_ON);
-               g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-
-               if (0 <= state && state < AltosUIMap.stateColors.length)
-                       g.setColor(AltosUIMap.stateColors[state]);
-               else
-                       g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
-
-               g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
-               g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
-               g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
-       }
-
-       public AltosUIMapMark (double lat, double lon, int state) {
-               lat_lon = new AltosUILatLon(lat, lon);
-               this.state = state;
-       }
-}
diff --git a/altosuilib/AltosUIMapNew.java b/altosuilib/AltosUIMapNew.java
new file mode 100644 (file)
index 0000000..016a202
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * 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 org.altusmetrum.altosuilib_8;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.image.*;
+import javax.swing.*;
+import java.io.*;
+import java.lang.Math;
+import java.awt.geom.*;
+import java.util.*;
+import java.util.concurrent.*;
+import javax.imageio.*;
+import org.altusmetrum.altoslib_8.*;
+
+public class AltosUIMapNew extends JComponent implements AltosFlightDisplay, AltosMapInterface {
+
+       AltosMap        map;
+       Graphics2D      g;
+       Font            tile_font;
+       Font            line_font;
+
+       static Point2D.Double point2d(AltosPointDouble pt) {
+               return new Point2D.Double(pt.x, pt.y);
+       }
+
+       static final AltosPointDouble point_double(Point pt) {
+               return new AltosPointDouble(pt.x, pt.y);
+       }
+
+       class MapMark extends AltosMapMark {
+               public void paint(AltosMapTransform t) {
+                       AltosPointDouble pt = t.screen(lat_lon);
+
+                       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                          RenderingHints.VALUE_ANTIALIAS_ON);
+                       g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+                       if (0 <= state && state < AltosUIMapNew.stateColors.length)
+                               g.setColor(AltosUIMapNew.stateColors[state]);
+                       else
+                               g.setColor(AltosUIMapNew.stateColors[AltosLib.ao_flight_invalid]);
+
+                       g.drawOval((int)pt.x-5, (int)pt.y-5, 10, 10);
+                       g.drawOval((int)pt.x-20, (int)pt.y-20, 40, 40);
+                       g.drawOval((int)pt.x-35, (int)pt.y-35, 70, 70);
+               }
+
+               MapMark(double lat, double lon, int state) {
+                       super(lat, lon, state);
+               }
+       }
+
+       class MapView extends JComponent implements MouseMotionListener, MouseListener, ComponentListener, MouseWheelListener {
+
+               private VolatileImage create_back_buffer() {
+                       return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
+               }
+
+               private void do_paint(Graphics my_g) {
+                       g = (Graphics2D) my_g;
+
+                       map.paint();
+               }
+
+               public void paint(Graphics my_g) {
+                       VolatileImage   back_buffer = create_back_buffer();
+
+                       Graphics2D      top_g = (Graphics2D) my_g;
+
+                       do {
+                               GraphicsConfiguration gc = getGraphicsConfiguration();
+                               int code = back_buffer.validate(gc);
+                               if (code == VolatileImage.IMAGE_INCOMPATIBLE)
+                                       back_buffer = create_back_buffer();
+
+                               Graphics g_back = back_buffer.getGraphics();
+                               g_back.setClip(top_g.getClip());
+                               do_paint(g_back);
+                               g_back.dispose();
+
+                               top_g.drawImage(back_buffer, 0, 0, this);
+                       } while (back_buffer.contentsLost());
+                       back_buffer.flush();
+               }
+
+               public void repaint(AltosRectangle damage) {
+                       repaint(damage.x, damage.y, damage.width, damage.height);
+               }
+
+               private boolean is_drag_event(MouseEvent e) {
+                       return e.getModifiers() == InputEvent.BUTTON1_MASK;
+               }
+
+               /* MouseMotionListener methods */
+
+               public void mouseDragged(MouseEvent e) {
+                       map.touch_continue(e.getPoint().x, e.getPoint().y, is_drag_event(e));
+               }
+
+               public void mouseMoved(MouseEvent e) {
+               }
+
+               /* MouseListener methods */
+               public void mouseClicked(MouseEvent e) {
+               }
+
+               public void mouseEntered(MouseEvent e) {
+               }
+
+               public void mouseExited(MouseEvent e) {
+               }
+
+               public void mousePressed(MouseEvent e) {
+                       map.touch_start(e.getPoint().x, e.getPoint().y, is_drag_event(e));
+               }
+
+               public void mouseReleased(MouseEvent e) {
+               }
+
+               /* MouseWheelListener methods */
+
+               public void mouseWheelMoved(MouseWheelEvent e) {
+                       int     zoom_change = e.getWheelRotation();
+
+                       map.set_zoom_centre(map.get_zoom() - zoom_change, new AltosPointInt(e.getPoint().x, e.getPoint().y));
+               }
+
+               /* ComponentListener methods */
+
+               public void componentHidden(ComponentEvent e) {
+               }
+
+               public void componentMoved(ComponentEvent e) {
+               }
+
+               public void componentResized(ComponentEvent e) {
+                       map.set_transform();
+               }
+
+               public void componentShown(ComponentEvent e) {
+                       map.set_transform();
+               }
+
+               MapView() {
+                       addComponentListener(this);
+                       addMouseMotionListener(this);
+                       addMouseListener(this);
+                       addMouseWheelListener(this);
+               }
+       }
+
+       class MapLine extends AltosMapLine {
+
+               public void paint(AltosMapTransform t) {
+
+                       if (start == null || end == null)
+                               return;
+
+                       g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+
+                       Line2D.Double line = new Line2D.Double(point2d(t.screen(start)),
+                                                              point2d(t.screen(end)));
+
+                       g.setColor(Color.WHITE);
+                       g.setStroke(new BasicStroke(stroke_width+4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+                       g.draw(line);
+
+                       g.setColor(Color.BLUE);
+                       g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+                       g.draw(line);
+
+                       String  message = line_dist();
+                       Rectangle2D     bounds;
+                       bounds = line_font.getStringBounds(message, g.getFontRenderContext());
+
+                       float x = (float) line.x1;
+                       float y = (float) line.y1 + (float) bounds.getHeight() / 2.0f;
+
+                       if (line.x1 < line.x2) {
+                               x -= (float) bounds.getWidth() + 2.0f;
+                       } else {
+                               x += 2.0f;
+                       }
+
+                       g.setFont(line_font);
+                       g.setColor(Color.WHITE);
+                       for (int dy = -2; dy <= 2; dy += 2)
+                               for (int dx = -2; dx <= 2; dx += 2)
+                                       g.drawString(message, x + dx, y + dy);
+                       g.setColor(Color.BLUE);
+                       g.drawString(message, x, y);
+               }
+
+               public MapLine() {
+               }
+       }
+
+       class MapPath extends AltosMapPath {
+               public void paint(AltosMapTransform t) {
+                       Point2D.Double  prev = null;
+
+                       g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+                                          RenderingHints.VALUE_ANTIALIAS_ON);
+                       g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+                       for (AltosMapPathPoint point : points) {
+                               Point2D.Double  cur = point2d(t.screen(point.lat_lon));
+                               if (prev != null) {
+                                       Line2D.Double   line = new Line2D.Double (prev, cur);
+                                       Rectangle       bounds = line.getBounds();
+
+                                       if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) {
+                                               if (0 <= point.state && point.state < AltosUIMapNew.stateColors.length)
+                                                       g.setColor(AltosUIMapNew.stateColors[point.state]);
+                                               else
+                                                       g.setColor(AltosUIMapNew.stateColors[AltosLib.ao_flight_invalid]);
+
+                                               g.draw(line);
+                                       }
+                               }
+                               prev = cur;
+                       }
+               }
+       }
+
+       class MapTile extends AltosMapTile {
+               public MapTile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+                       super(listener, upper_left, center, zoom, maptype, px_size);
+               }
+
+               public void paint(AltosMapTransform t) {
+
+                       AltosPointDouble        point_double = t.screen(upper_left);
+                       Point                   point = new Point((int) (point_double.x + 0.5),
+                                                                 (int) (point_double.y + 0.5));
+
+                       if (!g.hitClip(point.x, point.y, px_size, px_size))
+                               return;
+
+                       AltosImage altos_image = cache.get(this, store, px_size, px_size);
+
+                       AltosUIImage    ui_image = (AltosUIImage) altos_image;
+
+                       Image image = null;
+
+                       if (ui_image != null)
+                               image = ui_image.image;
+
+                       if (image != null) {
+                               g.drawImage(image, point.x, point.y, null);
+                       } else {
+                               g.setColor(Color.GRAY);
+                               g.fillRect(point.x, point.y, px_size, px_size);
+
+                               if (t.has_location()) {
+                                       String  message = null;
+                                       switch (status) {
+                                       case AltosMapTile.loading:
+                                               message = "Loading...";
+                                               break;
+                                       case AltosMapTile.bad_request:
+                                               message = "Internal error";
+                                               break;
+                                       case AltosMapTile.failed:
+                                               message = "Network error, check connection";
+                                               break;
+                                       case AltosMapTile.forbidden:
+                                               message = "Too many requests, try later";
+                                               break;
+                                       }
+                                       if (message != null && tile_font != null) {
+                                               g.setFont(tile_font);
+                                               g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
+                                               Rectangle2D bounds = tile_font.getStringBounds(message, g.getFontRenderContext());
+
+                                               float x = px_size / 2.0f;
+                                               float y = px_size / 2.0f;
+                                               x = x - (float) bounds.getWidth() / 2.0f;
+                                               y = y + (float) bounds.getHeight() / 2.0f;
+                                               g.setColor(Color.BLACK);
+                                               g.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       public static final 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
+               Color.BLACK,  // invalid
+               Color.CYAN,   // stateless
+       };
+
+       /* AltosMapInterface functions */
+
+       public AltosMapPath new_path() {
+               return new MapPath();
+       }
+
+       public AltosMapLine new_line() {
+               return new MapLine();
+       }
+
+       public AltosImage load_image(File file) throws Exception {
+               return new AltosUIImage(ImageIO.read(file));
+       }
+
+       public AltosMapMark new_mark(double lat, double lon, int state) {
+               return new MapMark(lat, lon, state);
+       }
+
+       public AltosMapTile new_tile(AltosMapTileListener listener, AltosLatLon upper_left, AltosLatLon center, int zoom, int maptype, int px_size) {
+               return new MapTile(listener, upper_left, center, zoom, maptype, px_size);
+       }
+
+       public int width() {
+               return view.getWidth();
+       }
+
+       public int height() {
+               return view.getHeight();
+       }
+
+       public void repaint() {
+               view.repaint();
+       }
+
+       public void repaint(AltosRectangle damage) {
+               view.repaint(damage);
+       }
+
+       public void set_zoom_label(String label) {
+               zoom_label.setText(label);
+       }
+
+       public void select_object(AltosLatLon latlon) {
+               debug("select at %f,%f\n", latlon.lat, latlon.lon);
+       }
+
+       public void debug(String format, Object ... arguments) {
+               System.out.printf(format, arguments);
+       }
+
+
+       /* AltosFlightDisplay interface */
+
+       public void set_font() {
+               tile_font = AltosUILib.value_font;
+               line_font = AltosUILib.status_font;
+       }
+
+       public void font_size_changed(int font_size) {
+               set_font();
+               repaint();
+       }
+
+       public void units_changed(boolean imperial_units) {
+               repaint();
+       }
+
+       JLabel  zoom_label;
+
+       public void set_maptype(int type) {
+               map.set_maptype(type);
+               maptype_combo.setSelectedIndex(type);
+       }
+
+       /* AltosUIMapPreload functions */
+
+       public void set_zoom(int zoom) {
+               map.set_zoom(zoom);
+       }
+
+       public void add_mark(double lat, double lon, int status) {
+               map.add_mark(lat, lon, status);
+       }
+
+       public void clear_marks() {
+               map.clear_marks();
+       }
+
+       /* AltosFlightDisplay interface */
+       public void reset() {
+               // nothing
+       }
+
+       public void show(AltosState state, AltosListenerState listener_state) {
+               map.show(state, listener_state);
+       }
+
+       public String getName() {
+               return "Map";
+       }
+
+       /* AltosGraphUI interface */
+       public void centre(AltosState state) {
+               map.centre(state);
+       }
+
+       /* internal layout bits */
+       private GridBagLayout layout = new GridBagLayout();
+
+       JComboBox<String>       maptype_combo;
+
+       MapView view;
+
+       public AltosUIMapNew() {
+
+               set_font();
+
+               view = new MapView();
+
+               view.setPreferredSize(new Dimension(500,500));
+               view.setVisible(true);
+               view.setEnabled(true);
+
+               GridBagLayout   my_layout = new GridBagLayout();
+
+               setLayout(my_layout);
+
+               GridBagConstraints c = new GridBagConstraints();
+               c.anchor = GridBagConstraints.CENTER;
+               c.fill = GridBagConstraints.BOTH;
+               c.gridx = 0;
+               c.gridy = 0;
+               c.gridwidth = 1;
+               c.gridheight = 10;
+               c.weightx = 1;
+               c.weighty = 1;
+               add(view, c);
+
+               int     y = 0;
+
+               zoom_label = new JLabel("", JLabel.CENTER);
+
+               c = new GridBagConstraints();
+               c.anchor = GridBagConstraints.CENTER;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.gridx = 1;
+               c.gridy = y++;
+               c.weightx = 0;
+               c.weighty = 0;
+               add(zoom_label, c);
+
+               JButton zoom_reset = new JButton("0");
+               zoom_reset.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       map.set_zoom(map.default_zoom);
+                               }
+                       });
+
+               c = new GridBagConstraints();
+               c.anchor = GridBagConstraints.CENTER;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.gridx = 1;
+               c.gridy = y++;
+               c.weightx = 0;
+               c.weighty = 0;
+               add(zoom_reset, c);
+
+               JButton zoom_in = new JButton("+");
+               zoom_in.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       map.set_zoom(map.get_zoom() + 1);
+                               }
+                       });
+
+               c = new GridBagConstraints();
+               c.anchor = GridBagConstraints.CENTER;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.gridx = 1;
+               c.gridy = y++;
+               c.weightx = 0;
+               c.weighty = 0;
+               add(zoom_in, c);
+
+               JButton zoom_out = new JButton("-");
+               zoom_out.addActionListener(new ActionListener() {
+                               public void actionPerformed(ActionEvent e) {
+                                       map.set_zoom(map.get_zoom() - 1);
+                               }
+                       });
+               c = new GridBagConstraints();
+               c.anchor = GridBagConstraints.CENTER;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.gridx = 1;
+               c.gridy = y++;
+               c.weightx = 0;
+               c.weighty = 0;
+               add(zoom_out, c);
+
+               maptype_combo = new JComboBox<String>(map.maptype_labels);
+
+               maptype_combo.setEditable(false);
+               maptype_combo.setMaximumRowCount(maptype_combo.getItemCount());
+               maptype_combo.addItemListener(new ItemListener() {
+                               public void itemStateChanged(ItemEvent e) {
+                                       map.set_maptype(maptype_combo.getSelectedIndex());
+                               }
+                       });
+
+               c = new GridBagConstraints();
+               c.anchor = GridBagConstraints.CENTER;
+               c.fill = GridBagConstraints.HORIZONTAL;
+               c.gridx = 1;
+               c.gridy = y++;
+               c.weightx = 0;
+               c.weighty = 0;
+               add(maptype_combo, c);
+
+               map = new AltosMap(this);
+       }
+}
diff --git a/altosuilib/AltosUIMapPath.java b/altosuilib/AltosUIMapPath.java
deleted file mode 100644 (file)
index e77af58..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-class PathPoint {
-       AltosUILatLon   lat_lon;
-       int             state;
-
-       public PathPoint(AltosUILatLon lat_lon, int state) {
-               this.lat_lon = lat_lon;
-               this.state = state;
-       }
-
-       public boolean equals(PathPoint other) {
-               if (other == null)
-                       return false;
-
-               return lat_lon.equals(other.lat_lon) && state == other.state;
-       }
-}
-
-public class AltosUIMapPath {
-
-       LinkedList<PathPoint>   points = new LinkedList<PathPoint>();
-       PathPoint               last_point = null;
-
-       static public int stroke_width = 6;
-
-       public void paint(Graphics2D g, AltosUIMapTransform t) {
-               Point2D.Double  prev = null;
-
-               g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
-                                  RenderingHints.VALUE_ANTIALIAS_ON);
-               g.setStroke(new BasicStroke(stroke_width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
-
-               for (PathPoint point : points) {
-                       Point2D.Double  cur = t.screen(point.lat_lon);
-                       if (prev != null) {
-                               Line2D.Double   line = new Line2D.Double (prev, cur);
-                               Rectangle       bounds = line.getBounds();
-
-                               if (g.hitClip(bounds.x, bounds.y, bounds.width, bounds.height)) {
-                                       if (0 <= point.state && point.state < AltosUIMap.stateColors.length)
-                                               g.setColor(AltosUIMap.stateColors[point.state]);
-                                       else
-                                               g.setColor(AltosUIMap.stateColors[AltosLib.ao_flight_invalid]);
-
-                                       g.draw(line);
-                               }
-                       }
-                       prev = cur;
-               }
-       }
-
-       public AltosUIMapRectangle add(double lat, double lon, int state) {
-               PathPoint               point = new PathPoint(new AltosUILatLon (lat, lon), state);
-               AltosUIMapRectangle     rect = null;
-
-               if (!point.equals(last_point)) {
-                       if (last_point != null)
-                               rect = new AltosUIMapRectangle(last_point.lat_lon, point.lat_lon);
-                       points.add (point);
-                       last_point = point;
-               }
-               return rect;
-       }
-
-       public void clear () {
-               points = new LinkedList<PathPoint>();
-       }
-}
diff --git a/altosuilib/AltosUIMapPreload.java b/altosuilib/AltosUIMapPreload.java
deleted file mode 100644 (file)
index e82b6c6..0000000
+++ /dev/null
@@ -1,607 +0,0 @@
-/*
- * 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.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.util.*;
-import java.text.*;
-import java.lang.Math;
-import java.net.URL;
-import java.net.URLConnection;
-import org.altusmetrum.altoslib_6.*;
-
-class AltosUIMapPos extends Box {
-       AltosUIFrame    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 AltosUIMapPos(AltosUIFrame 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<String>(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 AltosUISite {
-       String  name;
-       double  latitude;
-       double  longitude;
-
-       public String toString() {
-               return name;
-       }
-
-       public AltosUISite(String in_name, double in_latitude, double in_longitude) {
-               name = in_name;
-               latitude = in_latitude;
-               longitude = in_longitude;
-       }
-
-       public AltosUISite(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 AltosUISites extends Thread {
-       AltosUIMapPreload       preload;
-       URL                     url;
-       LinkedList<AltosUISite> sites;
-
-       void notify_complete() {
-               SwingUtilities.invokeLater(new Runnable() {
-                               public void run() {
-                                       preload.set_sites();
-                               }
-                       });
-       }
-
-       void add(AltosUISite site) {
-               sites.add(site);
-       }
-
-       void add(String line) {
-               try {
-                       add(new AltosUISite(line));
-               } catch (ParseException pe) {
-               }
-       }
-
-       public void run() {
-               try {
-                       URLConnection uc = url.openConnection();
-                       //int length = uc.getContentLength();
-
-                       InputStreamReader in_stream = new InputStreamReader(uc.getInputStream(), AltosLib.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 AltosUISites(AltosUIMapPreload in_preload) {
-               sites = new LinkedList<AltosUISite>();
-               preload = in_preload;
-               try {
-                       url = new URL(AltosLib.launch_sites_url);
-               } catch (java.net.MalformedURLException e) {
-                       notify_complete();
-               }
-               start();
-       }
-}
-
-public class AltosUIMapPreload extends AltosUIFrame implements ActionListener, ItemListener, AltosUIMapTileListener {
-       AltosUIFrame    owner;
-       AltosUIMap      map;
-       AltosUIMapCache cache = new AltosUIMapCache();
-
-       AltosUIMapPos   lat;
-       AltosUIMapPos   lon;
-
-       JProgressBar    pbar;
-       int             pbar_max;
-       int             pbar_cur;
-
-       AltosUISites    sites;
-       JLabel          site_list_label;
-       JComboBox<AltosUISite>  site_list;
-
-       JToggleButton   load_button;
-       boolean         loading;
-       JButton         close_button;
-
-       JCheckBox[]     maptypes = new JCheckBox[AltosUIMap.maptype_terrain - AltosUIMap.maptype_hybrid + 1];
-
-       JComboBox<Integer>      min_zoom;
-       JComboBox<Integer>      max_zoom;
-       JComboBox<Integer>      radius;
-
-       Integer[]               zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 };
-       Integer[]               radii = { 1, 2, 3, 4, 5 };
-
-       static final String[]   lat_hemi_names = { "N", "S" };
-       static final String[]   lon_hemi_names = { "E", "W" };
-
-       class updatePbar implements Runnable {
-               String          s;
-
-               public updatePbar(String in_s) {
-                       s = in_s;
-               }
-
-               public void run() {
-                       int     n = ++pbar_cur;
-
-                       pbar.setMaximum(pbar_max);
-                       pbar.setValue(n);
-                       pbar.setString(s);
-               }
-       }
-
-       double  latitude, longitude;
-       int     min_z;
-       int     max_z;
-       int     cur_z;
-       int     all_types;
-       int     cur_type;
-       int     r;
-
-       int     tiles_per_layer;
-       int     tiles_loaded;
-       int     layers_total;
-       int     layers_loaded;
-
-
-       private void do_load() {
-               tiles_loaded = 0;
-               map.set_zoom(cur_z + AltosUIMapView.default_zoom);
-               map.set_maptype(cur_type);
-               map.set_load_params(latitude, longitude, r, this);
-       }
-
-       private int next_type(int start) {
-               int next_type;
-               for (next_type = start;
-                    next_type <= AltosUIMap.maptype_terrain && (all_types & (1 << next_type)) == 0;
-                    next_type++)
-                       ;
-               return next_type;
-       }
-
-       private void next_load() {
-               int next_type = next_type(cur_type + 1);
-
-               if (next_type > AltosUIMap.maptype_terrain) {
-                       if (cur_z == max_z) {
-                               return;
-                       } else {
-                               cur_z++;
-                       }
-                       next_type = next_type(0);
-               }
-               cur_type = next_type;
-               do_load();
-       }
-
-       private void start_load() {
-               cur_z = min_z;
-               int ntype = 0;
-               all_types = 0;
-               for (int t = AltosUIMap.maptype_hybrid; t <= AltosUIMap.maptype_terrain; t++)
-                       if (maptypes[t].isSelected()) {
-                               all_types |= (1 << t);
-                               ntype++;
-                       }
-               if (ntype == 0) {
-                       all_types |= (1 << AltosUIMap.maptype_hybrid);
-                       ntype = 1;
-               }
-
-               cur_type = next_type(0);
-               tiles_per_layer = (r * 2 + 1) * (r * 2 + 1);
-               layers_total = (max_z - min_z + 1) * ntype;
-               layers_loaded = 0;
-               pbar_max = layers_total * tiles_per_layer;
-               pbar_cur = 0;
-
-               map.clear_marks();
-               map.add_mark(latitude,longitude, AltosLib.ao_flight_boost);
-               do_load();
-       }
-
-       /* AltosUIMapTileListener methods */
-
-       public synchronized void notify_tile(AltosUIMapTile tile, int status) {
-               if (status == AltosUIMapStore.loading)
-                       return;
-
-               SwingUtilities.invokeLater(new updatePbar(tile.store.file.toString()));
-               ++tiles_loaded;
-               if (tiles_loaded == tiles_per_layer) {
-                       ++layers_loaded;
-                       if (layers_loaded == layers_total) {
-                               SwingUtilities.invokeLater(new Runnable() {
-                                               public void run() {
-                                                       pbar.setValue(0);
-                                                       pbar.setString("");
-                                                       load_button.setSelected(false);
-                                                       loading = false;
-                                               }
-                                       });
-                       } else {
-                               SwingUtilities.invokeLater(new Runnable() {
-                                               public void run() {
-                                                       next_load();
-                                               }
-                                       });
-                       }
-               }
-       }
-
-       public AltosUIMapCache cache() { return cache; }
-
-       public void set_sites() {
-               int     i = 1;
-               for (AltosUISite 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 AltosUISite) {
-                               AltosUISite     site = (AltosUISite) 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 {
-                                       latitude = lat.get_value();
-                                       longitude = lon.get_value();
-                                       min_z = (Integer) min_zoom.getSelectedItem();
-                                       max_z = (Integer) max_zoom.getSelectedItem();
-                                       if (max_z < min_z)
-                                               max_z = min_z;
-                                       r = (Integer) radius.getSelectedItem();
-                                       loading = true;
-                               } catch (NumberFormatException ne) {
-                                       load_button.setSelected(false);
-                               }
-                               start_load();
-                       }
-               }
-       }
-
-       public AltosUIMapPreload(AltosUIFrame in_owner) {
-               owner = in_owner;
-
-               Container               pane = getContentPane();
-               GridBagConstraints      c = new GridBagConstraints();
-               Insets                  i = new Insets(4,4,4,4);
-
-               setTitle("AltOS Load Maps");
-
-               pane.setLayout(new GridBagLayout());
-
-               map = new AltosUIMap();
-
-               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 = 10;
-               c.anchor = GridBagConstraints.CENTER;
-
-               pane.add(map, c);
-
-               pbar = new JProgressBar();
-               pbar.setMinimum(0);
-               pbar.setMaximum(1);
-               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 = 10;
-
-               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<AltosUISite>(new AltosUISite[] { new AltosUISite("Site List", 0, 0) });
-               site_list.addItemListener(this);
-
-               sites = new AltosUISites(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 AltosUIMapPos(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 AltosUIMapPos(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);
-
-               JLabel  types_label = new JLabel("Map Types");
-               c.gridx = 2;
-               c.gridwidth = 2;
-               c.gridy = 2;
-               pane.add(types_label, c);
-
-               c.gridwidth = 1;
-
-               for (int type = AltosUIMap.maptype_hybrid; type <= AltosUIMap.maptype_terrain; type++) {
-                       maptypes[type] = new JCheckBox(AltosUIMap.maptype_labels[type],
-                                                      type == AltosUIMap.maptype_hybrid);
-                       c.gridx = 2 + (type >> 1);
-                       c.fill = GridBagConstraints.HORIZONTAL;
-                       c.gridy = (type & 1) + 3;
-                       pane.add(maptypes[type], c);
-               }
-
-               JLabel  min_zoom_label = new JLabel("Minimum Zoom");
-               c.gridx = 4;
-               c.gridy = 2;
-               pane.add(min_zoom_label, c);
-
-               min_zoom = new JComboBox<Integer>(zooms);
-               min_zoom.setSelectedItem(zooms[10]);
-               min_zoom.setEditable(false);
-               c.gridx = 5;
-               c.gridy = 2;
-               pane.add(min_zoom, c);
-
-               JLabel  max_zoom_label = new JLabel("Maximum Zoom");
-               c.gridx = 4;
-               c.gridy = 3;
-               pane.add(max_zoom_label, c);
-
-               max_zoom = new JComboBox<Integer>(zooms);
-               max_zoom.setSelectedItem(zooms[14]);
-               max_zoom.setEditable(false);
-               c.gridx = 5;
-               c.gridy = 3;
-               pane.add(max_zoom, c);
-
-               JLabel radius_label = new JLabel("Tile Radius");
-               c.gridx = 4;
-               c.gridy = 4;
-               pane.add(radius_label, c);
-
-               radius = new JComboBox<Integer>(radii);
-               radius.setSelectedItem(radii[4]);
-               radius.setEditable(true);
-               c.gridx = 5;
-               c.gridy = 4;
-               pane.add(radius, c);
-
-               pack();
-               setLocationRelativeTo(owner);
-               setVisible(true);
-       }
-}
diff --git a/altosuilib/AltosUIMapPreloadNew.java b/altosuilib/AltosUIMapPreloadNew.java
new file mode 100644 (file)
index 0000000..2d4d45b
--- /dev/null
@@ -0,0 +1,472 @@
+/*
+ * 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.altosuilib_8;
+
+import java.awt.*;
+import java.awt.event.*;
+import javax.swing.*;
+import java.io.*;
+import java.util.*;
+import java.text.*;
+import java.lang.Math;
+import java.net.URL;
+import java.net.URLConnection;
+import org.altusmetrum.altoslib_8.*;
+
+class AltosUIMapPos extends Box {
+       AltosUIFrame    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 ParseException {
+               int     h = hemi.getSelectedIndex();
+               String  d_t = deg.getText();
+               String  m_t = min.getText();
+               double  d, m, v;
+               try {
+                       d = AltosParse.parse_double_locale(d_t);
+               } catch (ParseException pe) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid degrees \"%s\"",
+                                                                   d_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw pe;
+               }
+               try {
+                       if (m_t.equals(""))
+                               m = 0;
+                       else
+                               m = AltosParse.parse_double_locale(m_t);
+               } catch (ParseException pe) {
+                       JOptionPane.showMessageDialog(owner,
+                                                     String.format("Invalid minutes \"%s\"",
+                                                                   m_t),
+                                                     "Invalid number",
+                                                     JOptionPane.ERROR_MESSAGE);
+                       throw pe;
+               }
+               v = d + m/60.0;
+               if (h == 1)
+                       v = -v;
+               return v;
+       }
+
+       public AltosUIMapPos(AltosUIFrame 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<String>(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);
+       }
+}
+
+public class AltosUIMapPreloadNew extends AltosUIFrame implements ActionListener, ItemListener, AltosLaunchSiteListener, AltosMapLoaderListener  {
+       AltosUIFrame    owner;
+       AltosUIMapNew   map;
+
+       AltosUIMapPos   lat;
+       AltosUIMapPos   lon;
+
+       JProgressBar    pbar;
+
+       AltosMapLoader  loader;
+
+       JLabel          site_list_label;
+       JComboBox<AltosLaunchSite>      site_list;
+
+       JToggleButton   load_button;
+       boolean         loading;
+       JButton         close_button;
+
+       JCheckBox[]     maptypes = new JCheckBox[AltosMap.maptype_terrain - AltosMap.maptype_hybrid + 1];
+
+       JComboBox<Integer>      min_zoom;
+       JComboBox<Integer>      max_zoom;
+       JComboBox<Double>       radius;
+
+       Integer[]               zooms = { -12, -11, -10, -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6 };
+
+       Double[]        radius_mi = { 1.0, 2.0, 5.0, 10.0, 20.0 };
+       Double          radius_def_mi = 5.0;
+       Double[]        radius_km = { 2.0, 5.0, 10.0, 20.0, 30.0 };
+       Double          radius_def_km = 10.0;
+
+
+       static final String[]   lat_hemi_names = { "N", "S" };
+       static final String[]   lon_hemi_names = { "E", "W" };
+
+       double  latitude, longitude;
+
+       /* AltosMapLoaderListener interfaces */
+       public void loader_start(final int max) {
+               SwingUtilities.invokeLater(new Runnable() {
+                               public void run() {
+                                       pbar.setMaximum(max);
+                                       pbar.setValue(0);
+                                       pbar.setString("");
+                                       map.clear_marks();
+                                       map.add_mark(latitude, longitude, AltosLib.ao_flight_boost);
+                               }
+                       });
+       }
+
+       public void loader_notify(final int cur, final int max, final String name) {
+               SwingUtilities.invokeLater(new Runnable() {
+                               public void run() {
+                                       pbar.setValue(cur);
+                                       pbar.setString(name);
+                               }
+                       });
+       }
+
+       public void loader_done(int max) {
+               SwingUtilities.invokeLater(new Runnable() {
+                               public void run() {
+                                       pbar.setValue(0);
+                                       pbar.setString("");
+                                       load_button.setSelected(false);
+                                       loading = false;
+                               }
+                       });
+       }
+
+       public void debug(String format, Object ... arguments) {
+               System.out.printf(format, arguments);
+       }
+
+
+       private int all_types() {
+               int all_types = 0;
+               for (int t = AltosMap.maptype_hybrid; t <= AltosMap.maptype_terrain; t++)
+                       if (maptypes[t].isSelected())
+                               all_types |= (1 << t);
+               return all_types;
+       }
+
+       public void itemStateChanged(ItemEvent e) {
+               int             state = e.getStateChange();
+
+               if (state == ItemEvent.SELECTED) {
+                       Object  o = e.getItem();
+                       if (o instanceof AltosLaunchSite) {
+                               AltosLaunchSite site = (AltosLaunchSite) 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 {
+                                       latitude = lat.get_value();
+                                       longitude = lon.get_value();
+                                       int min_z = (Integer) min_zoom.getSelectedItem();
+                                       int max_z = (Integer) max_zoom.getSelectedItem();
+                                       if (max_z < min_z)
+                                               max_z = min_z;
+                                       Double r = (Double) radius.getSelectedItem();
+
+                                       if (AltosPreferences.imperial_units())
+                                               r = AltosConvert.distance.inverse(r);
+                                       else
+                                               r = r * 1000;
+                                       loading = true;
+
+                                       loader.load(latitude, longitude, min_z, max_z, r, all_types());
+                               } catch (ParseException pe) {
+                                       load_button.setSelected(false);
+                               }
+                       }
+               }
+       }
+
+       public void notify_launch_sites(final java.util.List<AltosLaunchSite> sites) {
+               SwingUtilities.invokeLater(new Runnable() {
+                               public void run() {
+                                       int     i = 1;
+                                       for (AltosLaunchSite site : sites) {
+                                               site_list.insertItemAt(site, i);
+                                               i++;
+                                       }
+                               }
+                       });
+       }
+
+       public AltosUIMapPreloadNew(AltosUIFrame in_owner) {
+               owner = in_owner;
+
+               Container               pane = getContentPane();
+               GridBagConstraints      c = new GridBagConstraints();
+               Insets                  i = new Insets(4,4,4,4);
+
+               setTitle("AltOS Load Maps");
+
+               pane.setLayout(new GridBagLayout());
+
+               map = new AltosUIMapNew();
+
+               loader = new AltosMapLoader(map.map, this);
+
+               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 = 10;
+               c.anchor = GridBagConstraints.CENTER;
+
+               pane.add(map, c);
+
+               pbar = new JProgressBar();
+               pbar.setMinimum(0);
+               pbar.setMaximum(1);
+               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 = 10;
+
+               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<AltosLaunchSite>(new AltosLaunchSite[] { new AltosLaunchSite("Site List", 0, 0) });
+               site_list.addItemListener(this);
+
+               new AltosLaunchSites(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 AltosUIMapPos(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 AltosUIMapPos(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);
+
+               JLabel  types_label = new JLabel("Map Types");
+               c.gridx = 2;
+               c.gridwidth = 2;
+               c.gridy = 2;
+               pane.add(types_label, c);
+
+               c.gridwidth = 1;
+
+               for (int type = AltosMap.maptype_hybrid; type <= AltosMap.maptype_terrain; type++) {
+                       maptypes[type] = new JCheckBox(AltosMap.maptype_labels[type],
+                                                      type == AltosMap.maptype_hybrid);
+                       c.gridx = 2 + (type >> 1);
+                       c.fill = GridBagConstraints.HORIZONTAL;
+                       c.gridy = (type & 1) + 3;
+                       pane.add(maptypes[type], c);
+               }
+
+               JLabel  min_zoom_label = new JLabel("Minimum Zoom");
+               c.gridx = 4;
+               c.gridy = 2;
+               pane.add(min_zoom_label, c);
+
+               min_zoom = new JComboBox<Integer>(zooms);
+               min_zoom.setSelectedItem(zooms[10]);
+               min_zoom.setEditable(false);
+               c.gridx = 5;
+               c.gridy = 2;
+               pane.add(min_zoom, c);
+
+               JLabel  max_zoom_label = new JLabel("Maximum Zoom");
+               c.gridx = 4;
+               c.gridy = 3;
+               pane.add(max_zoom_label, c);
+
+               max_zoom = new JComboBox<Integer>(zooms);
+               max_zoom.setSelectedItem(zooms[14]);
+               max_zoom.setEditable(false);
+               c.gridx = 5;
+               c.gridy = 3;
+               pane.add(max_zoom, c);
+
+               JLabel radius_label = new JLabel(String.format("Map Radius (%s)",
+                                                              AltosPreferences.imperial_units() ? "miles" : "km"));
+               c.gridx = 4;
+               c.gridy = 4;
+               pane.add(radius_label, c);
+
+               Double[]        radii;
+               Double          radius_default;
+
+               if (AltosPreferences.imperial_units())
+                       radii = radius_mi;
+               else
+                       radii = radius_km;
+               radius = new JComboBox<Double>(radii);
+               radius.setSelectedItem(radii[2]);
+               radius.setEditable(true);
+               c.gridx = 5;
+               c.gridy = 4;
+               pane.add(radius, c);
+
+               pack();
+               setLocationRelativeTo(owner);
+               setVisible(true);
+       }
+}
diff --git a/altosuilib/AltosUIMapRectangle.java b/altosuilib/AltosUIMapRectangle.java
deleted file mode 100644 (file)
index dc0e4cc..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-public class AltosUIMapRectangle {
-       AltosUILatLon   ul, lr;
-
-       public AltosUIMapRectangle(AltosUILatLon a, AltosUILatLon b) {
-               double  ul_lat, ul_lon;
-               double  lr_lat, lr_lon;
-
-               if (a.lat > b.lat) {
-                       ul_lat = a.lat;
-                       lr_lat = b.lat;
-               } else {
-                       ul_lat = b.lat;
-                       lr_lat = a.lat;
-               }
-               if (a.lon < b.lon) {
-                       ul_lon = a.lon;
-                       lr_lon = b.lon;
-               } else {
-                       ul_lon = b.lon;
-                       lr_lon = a.lon;
-               }
-
-               ul = new AltosUILatLon(ul_lat, ul_lon);
-               lr = new AltosUILatLon(lr_lat, lr_lon);
-       }
-}
diff --git a/altosuilib/AltosUIMapStore.java b/altosuilib/AltosUIMapStore.java
deleted file mode 100644 (file)
index 70bb6fe..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-public class AltosUIMapStore {
-       String                                  url;
-       File                                    file;
-       LinkedList<AltosUIMapStoreListener>     listeners = new LinkedList<AltosUIMapStoreListener>();
-
-       static final int                        success = 0;
-       static final int                        loading = 1;
-       static final int                        failed = 2;
-       static final int                        bad_request = 3;
-       static final int                        forbidden = 4;
-
-       int                                     status;
-
-       public int status() {
-               return status;
-       }
-
-       public synchronized void add_listener(AltosUIMapStoreListener listener) {
-               if (!listeners.contains(listener))
-                       listeners.add(listener);
-       }
-
-       public synchronized void remove_listener(AltosUIMapStoreListener listener) {
-               listeners.remove(listener);
-       }
-
-       private synchronized void notify_listeners(int status) {
-               this.status = status;
-               for (AltosUIMapStoreListener listener : listeners)
-                       listener.notify_store(this, status);
-       }
-
-       static Object   forbidden_lock = new Object();
-       static long     forbidden_time;
-       static boolean  forbidden_set;
-
-       private int fetch_url() {
-               URL u;
-
-               try {
-                       u = new URL(url);
-               } catch (java.net.MalformedURLException e) {
-                       return bad_request;
-               }
-
-               byte[] data;
-               URLConnection uc = null;
-               try {
-                       uc = u.openConnection();
-                       String type = uc.getContentType();
-                       int contentLength = uc.getContentLength();
-                       if (uc instanceof HttpURLConnection) {
-                               int response = ((HttpURLConnection) uc).getResponseCode();
-                               switch (response) {
-                               case HttpURLConnection.HTTP_FORBIDDEN:
-                               case HttpURLConnection.HTTP_PAYMENT_REQUIRED:
-                               case HttpURLConnection.HTTP_UNAUTHORIZED:
-                                       synchronized (forbidden_lock) {
-                                               forbidden_time = System.nanoTime();
-                                               forbidden_set = true;
-                                               return forbidden;
-                                       }
-                               }
-                       }
-                       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 failed;
-
-               } catch (IOException e) {
-                       return failed;
-               }
-
-               try {
-                       FileOutputStream out = new FileOutputStream(file);
-                       out.write(data);
-                       out.flush();
-                       out.close();
-               } catch (FileNotFoundException e) {
-                       return bad_request;
-               } catch (IOException e) {
-                       if (file.exists())
-                               file.delete();
-                       return bad_request;
-               }
-               return success;
-       }
-
-       static Object   fetch_lock = new Object();
-
-       static final long       forbidden_interval = 60l * 1000l * 1000l * 1000l;
-       static final long       google_maps_ratelimit_ms = 1200;
-
-       class loader implements Runnable {
-
-               public void run() {
-                       if (file.exists()) {
-                               notify_listeners(success);
-                               return;
-                       }
-
-                       synchronized(forbidden_lock) {
-                               if (forbidden_set && (System.nanoTime() - forbidden_time) < forbidden_interval) {
-                                       notify_listeners(forbidden);
-                                       return;
-                               }
-                       }
-
-                       int new_status;
-
-                       if (!AltosUIVersion.has_google_maps_api_key()) {
-                               synchronized (fetch_lock) {
-                                       long startTime = System.nanoTime();
-                                       new_status = fetch_url();
-                                       if (new_status == success) {
-                                               long duration_ms = (System.nanoTime() - startTime) / 1000000;
-                                               if (duration_ms < google_maps_ratelimit_ms) {
-                                                       try {
-                                                               Thread.sleep(google_maps_ratelimit_ms - duration_ms);
-                                                       } catch (InterruptedException e) {
-                                                               Thread.currentThread().interrupt();
-                                                       }
-                                               }
-                                       }
-                               }
-                       } else {
-                               new_status = fetch_url();
-                       }
-                       notify_listeners(new_status);
-               }
-       }
-
-       private void load() {
-               loader  l = new loader();
-               Thread  lt = new Thread(l);
-               lt.start();
-       }
-
-       private AltosUIMapStore (String url, File file) {
-               this.url = url;
-               this.file = file;
-
-               if (file.exists())
-                       status = success;
-               else {
-                       status = loading;
-                       load();
-               }
-       }
-
-       public boolean equals(AltosUIMapStore other) {
-               return url.equals(other.url);
-       }
-
-       static HashMap<String,AltosUIMapStore> stores = new HashMap<String,AltosUIMapStore>();
-
-       public static AltosUIMapStore get(String url, File file) {
-               AltosUIMapStore store;
-               synchronized(stores) {
-                       if (stores.containsKey(url)) {
-                               store = stores.get(url);
-                       } else {
-                               store = new AltosUIMapStore(url, file);
-                               stores.put(url, store);
-                       }
-               }
-               return store;
-       }
-
-}
diff --git a/altosuilib/AltosUIMapStoreListener.java b/altosuilib/AltosUIMapStoreListener.java
deleted file mode 100644 (file)
index ccda898..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-public interface AltosUIMapStoreListener {
-       abstract void notify_store(AltosUIMapStore store, int status);
-}
diff --git a/altosuilib/AltosUIMapTile.java b/altosuilib/AltosUIMapTile.java
deleted file mode 100644 (file)
index afd1bbc..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * 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 org.altusmetrum.altosuilib_6;
-
-import java.awt.*;
-import java.awt.image.*;
-import javax.swing.*;
-import javax.imageio.*;
-import java.awt.geom.*;
-import java.io.*;
-import java.util.*;
-import java.awt.RenderingHints.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapTile {
-       AltosUIMapTileListener  listener;
-       AltosUILatLon   upper_left, center;
-       int             px_size;
-       int             zoom;
-       int             maptype;
-       AltosUIMapStore store;
-       AltosUIMapCache cache;
-       int             status;
-
-       private File map_file() {
-               double lat = center.lat;
-               double lon = center.lon;
-               char chlat = lat < 0 ? 'S' : 'N';
-               char chlon = lon < 0 ? 'W' : 'E';
-
-               if (lat < 0) lat = -lat;
-               if (lon < 0) lon = -lon;
-               String maptype_string = String.format("%s-", AltosUIMap.maptype_names[maptype]);
-               String format_string;
-               if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain)
-                       format_string = "jpg";
-               else
-                       format_string = "png";
-               return new File(AltosUIPreferences.mapdir(),
-                               String.format("map-%c%.6f,%c%.6f-%s%d.%s",
-                                             chlat, lat, chlon, lon, maptype_string, zoom, format_string));
-       }
-
-       private String map_url() {
-               String format_string;
-               if (maptype == AltosUIMap.maptype_hybrid || maptype == AltosUIMap.maptype_satellite || maptype == AltosUIMap.maptype_terrain)
-                       format_string = "jpg";
-               else
-                       format_string = "png32";
-
-               if (AltosUIVersion.has_google_maps_api_key())
-                       return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s&key=%s",
-                                            center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string, AltosUIVersion.google_maps_api_key);
-               else
-                       return String.format("http://maps.google.com/maps/api/staticmap?center=%.6f,%.6f&zoom=%d&size=%dx%d&sensor=false&maptype=%s&format=%s",
-                                            center.lat, center.lon, zoom, px_size, px_size, AltosUIMap.maptype_names[maptype], format_string);
-       }
-       private Font    font = null;
-
-       public void set_font(Font font) {
-               this.font = font;
-       }
-
-       int     painting_serial;
-       int     painted_serial;
-
-       Image   image;
-
-       public void paint_graphics(Graphics2D g2d, AltosUIMapTransform t, int serial) {
-               if (serial < painted_serial)
-                       return;
-
-               Point2D.Double  point_double = t.screen(upper_left);
-               Point           point = new Point((int) (point_double.x + 0.5),
-                                                 (int) (point_double.y + 0.5));
-
-               painted_serial = serial;
-
-               if (!g2d.hitClip(point.x, point.y, px_size, px_size))
-                       return;
-
-               if (image != null) {
-                       g2d.drawImage(image, point.x, point.y, null);
-                       image = null;
-               } else {
-                       g2d.setColor(Color.GRAY);
-                       g2d.fillRect(point.x, point.y, px_size, px_size);
-
-                       if (t.has_location()) {
-                               String  message = null;
-                               switch (status) {
-                               case AltosUIMapCache.loading:
-                                       message = "Loading...";
-                                       break;
-                               case AltosUIMapCache.bad_request:
-                                       message = "Internal error";
-                                       break;
-                               case AltosUIMapCache.failed:
-                                       message = "Network error, check connection";
-                                       break;
-                               case AltosUIMapCache.forbidden:
-                                       message = "Too many requests, try later";
-                                       break;
-                               }
-                               if (message != null && font != null) {
-                                       g2d.setFont(font);
-                                       g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
-                                       Rectangle2D bounds = font.getStringBounds(message, g2d.getFontRenderContext());
-
-                                       float x = px_size / 2.0f;
-                                       float y = px_size / 2.0f;
-                                       x = x - (float) bounds.getWidth() / 2.0f;
-                                       y = y + (float) bounds.getHeight() / 2.0f;
-                                       g2d.setColor(Color.BLACK);
-                                       g2d.drawString(message, (float) point_double.x + x, (float) point_double.y + y);
-                               }
-                       }
-               }
-       }
-
-       public void set_status(int status) {
-               this.status = status;
-               listener.notify_tile(this, status);
-       }
-
-       public void notify_image(Image image) {
-               listener.notify_tile(this, status);
-       }
-
-       public void paint(Graphics g, AltosUIMapTransform t) {
-               Graphics2D              g2d = (Graphics2D) g;
-               boolean                 queued = false;
-
-               Point2D.Double  point = t.screen(upper_left);
-
-               if (!g.hitClip((int) (point.x + 0.5), (int) (point.y + 0.5), px_size, px_size))
-                       return;
-
-               ++painting_serial;
-
-               if (image == null && t.has_location())
-                       image = cache.get(this, store, px_size, px_size);
-
-               paint_graphics(g2d, t, painting_serial);
-       }
-
-       public int store_status() {
-               return store.status();
-       }
-
-       public void add_store_listener(AltosUIMapStoreListener listener) {
-               store.add_listener(listener);
-       }
-
-       public void remove_store_listener(AltosUIMapStoreListener listener) {
-               store.remove_listener(listener);
-       }
-
-       public AltosUIMapTile(AltosUIMapTileListener listener, AltosUILatLon upper_left, AltosUILatLon center, int zoom, int maptype, int px_size, Font font) {
-               this.listener = listener;
-               this.upper_left = upper_left;
-               cache = listener.cache();
-
-               while (center.lon < -180.0)
-                       center.lon += 360.0;
-               while (center.lon > 180.0)
-                       center.lon -= 360.0;
-
-               this.center = center;
-               this.zoom = zoom;
-               this.maptype = maptype;
-               this.px_size = px_size;
-               this.font = font;
-               status = AltosUIMapCache.loading;
-               store = AltosUIMapStore.get(map_url(), map_file());
-       }
-}
diff --git a/altosuilib/AltosUIMapTileListener.java b/altosuilib/AltosUIMapTileListener.java
deleted file mode 100644 (file)
index dace5b7..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-public interface AltosUIMapTileListener {
-       abstract public void notify_tile(AltosUIMapTile tile, int status);
-
-       abstract public AltosUIMapCache cache();
-}
diff --git a/altosuilib/AltosUIMapTransform.java b/altosuilib/AltosUIMapTransform.java
deleted file mode 100644 (file)
index 2549740..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.Math;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapTransform {
-
-       double  scale_x, scale_y;
-
-       double  offset_x, offset_y;
-
-       public AltosUILatLon lat_lon (Point2D.Double point) {
-               double lat, lon;
-               double rads;
-
-               lon = point.x/scale_x;
-               rads = 2 * Math.atan(Math.exp(-point.y/scale_y));
-               lat = Math.toDegrees(rads - Math.PI/2);
-
-               return new AltosUILatLon(lat,lon);
-       }
-
-       public Point2D.Double screen_point(Point screen) {
-               return new Point2D.Double(screen.x + offset_x, screen.y + offset_y);
-       }
-
-       public AltosUILatLon screen_lat_lon(Point screen) {
-               return lat_lon(screen_point(screen));
-       }
-
-       public Point2D.Double point(AltosUILatLon lat_lon) {
-               double x, y;
-               double e;
-
-               x = lat_lon.lon * scale_x;
-
-               e = Math.sin(Math.toRadians(lat_lon.lat));
-               e = Math.max(e,-(1-1.0E-15));
-               e = Math.min(e,  1-1.0E-15 );
-
-               y = 0.5*Math.log((1+e)/(1-e))*-scale_y;
-
-               return new Point2D.Double(x, y);
-       }
-
-       public Point2D.Double screen(Point2D.Double point) {
-               return new Point2D.Double(point.x - offset_x, point.y - offset_y);
-       }
-
-       public Point screen(Point point) {
-               return new Point((int) (point.x - offset_x + 0.5),
-                                (int) (point.y - offset_y + 0.5));
-       }
-
-       public Rectangle screen(AltosUIMapRectangle map_rect) {
-               Point2D.Double  ul = screen(map_rect.ul);
-               Point2D.Double  lr = screen(map_rect.lr);
-
-               return new Rectangle((int) ul.x, (int) ul.y, (int) (lr.x - ul.x), (int) (lr.y - ul.y));
-       }
-
-       public Point2D.Double screen(AltosUILatLon lat_lon) {
-               return screen(point(lat_lon));
-       }
-
-       private boolean has_location;
-
-       public boolean has_location() {
-               return has_location;
-       }
-
-       public AltosUIMapTransform(int width, int height, int zoom, AltosUILatLon centre_lat_lon) {
-               scale_x = 256/360.0 * Math.pow(2, zoom);
-               scale_y = 256/(2.0*Math.PI) * Math.pow(2, zoom);
-
-               Point2D.Double centre_pt = point(centre_lat_lon);
-
-               has_location = (centre_lat_lon.lat != 0 || centre_lat_lon.lon != 0);
-               offset_x = centre_pt.x - width / 2.0;
-               offset_y = centre_pt.y - height / 2.0;
-       }
-}
diff --git a/altosuilib/AltosUIMapView.java b/altosuilib/AltosUIMapView.java
deleted file mode 100644 (file)
index c8632b9..0000000
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-import java.awt.*;
-import java.awt.event.*;
-import java.awt.image.*;
-import javax.swing.*;
-import java.io.*;
-import java.lang.*;
-import java.awt.geom.*;
-import java.util.*;
-import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
-
-public class AltosUIMapView extends Component implements MouseMotionListener, MouseListener, MouseWheelListener, ComponentListener, AltosUIMapTileListener, AltosUIMapStoreListener {
-
-       AltosUIMapPath  path = new AltosUIMapPath();
-
-       AltosUIMapLine  line = new AltosUIMapLine();
-
-       AltosUIMapCache cache = new AltosUIMapCache();
-
-       LinkedList<AltosUIMapMark> marks = new LinkedList<AltosUIMapMark>();
-
-       LinkedList<AltosUIMapZoomListener> zoom_listeners = new LinkedList<AltosUIMapZoomListener>();
-
-       boolean         have_boost = false;
-       boolean         have_landed = false;
-
-       ConcurrentHashMap<Point,AltosUIMapTile> tiles = new ConcurrentHashMap<Point,AltosUIMapTile>();
-
-       static final int default_zoom = 15;
-       static final int min_zoom = 3;
-       static final int max_zoom = 21;
-       static final int px_size = 512;
-
-       int             load_radius;
-       AltosUILatLon   load_centre = null;
-       AltosUIMapTileListener  load_listener;
-
-       int             zoom = default_zoom;
-       int             maptype = AltosUIMap.maptype_default;
-
-       long            user_input_time;
-
-       /* Milliseconds to wait after user action before auto-scrolling
-        */
-       static final long auto_scroll_delay = 20 * 1000;
-
-       AltosUIMapTransform     transform;
-       AltosUILatLon           centre;
-
-       public void set_font() {
-               line.set_font(AltosUILib.status_font);
-               for (AltosUIMapTile tile : tiles.values())
-                       tile.set_font(AltosUILib.value_font);
-               repaint();
-       }
-
-       public void set_units() {
-               repaint();
-       }
-
-       private boolean is_drag_event(MouseEvent e) {
-               return e.getModifiers() == InputEvent.BUTTON1_MASK;
-       }
-
-       Point   drag_start;
-
-       private void drag(MouseEvent e) {
-               if (drag_start == null)
-                       return;
-
-               int dx = e.getPoint().x - drag_start.x;
-               int dy = e.getPoint().y - drag_start.y;
-
-               AltosUILatLon   new_centre = transform.screen_lat_lon(new Point(getWidth() / 2 - dx, getHeight() / 2 - dy));
-               centre (new_centre.lat, new_centre.lon);
-               drag_start = e.getPoint();
-       }
-
-       private void drag_start(MouseEvent e) {
-               drag_start = e.getPoint();
-       }
-
-       private void notice_user_input() {
-               user_input_time = System.currentTimeMillis();
-       }
-
-       private boolean recent_user_input() {
-               return (System.currentTimeMillis() - user_input_time) < auto_scroll_delay;
-       }
-
-       /* MouseMotionListener methods */
-
-       public void mouseDragged(MouseEvent e) {
-               notice_user_input();
-               if (is_drag_event(e))
-                       drag(e);
-               else {
-                       line.dragged(e, transform);
-                       repaint();
-               }
-       }
-
-       public void mouseMoved(MouseEvent e) {
-       }
-
-       /* MouseListener methods */
-       public void mouseClicked(MouseEvent e) {
-       }
-
-       public void mouseEntered(MouseEvent e) {
-       }
-
-       public void mouseExited(MouseEvent e) {
-       }
-
-       public void mousePressed(MouseEvent e) {
-               notice_user_input();
-               if (is_drag_event(e))
-                       drag_start(e);
-               else {
-                       line.pressed(e, transform);
-                       repaint();
-               }
-       }
-
-       public void mouseReleased(MouseEvent e) {
-       }
-
-       /* MouseWheelListener methods */
-
-       public void mouseWheelMoved(MouseWheelEvent e) {
-               int     zoom_change = e.getWheelRotation();
-
-               notice_user_input();
-               AltosUILatLon   mouse_lat_lon = transform.screen_lat_lon(e.getPoint());
-               set_zoom(zoom() - zoom_change);
-
-               Point2D.Double  new_mouse = transform.screen(mouse_lat_lon);
-
-               int     dx = getWidth()/2 - e.getPoint().x;
-               int     dy = getHeight()/2 - e.getPoint().y;
-
-               AltosUILatLon   new_centre = transform.screen_lat_lon(new Point((int) new_mouse.x + dx, (int) new_mouse.y + dy));
-
-               centre(new_centre.lat, new_centre.lon);
-       }
-
-       /* ComponentListener methods */
-
-       public void componentHidden(ComponentEvent e) {
-       }
-
-       public void componentMoved(ComponentEvent e) {
-       }
-
-       public void componentResized(ComponentEvent e) {
-               set_transform();
-       }
-
-       public void componentShown(ComponentEvent e) {
-               set_transform();
-       }
-
-       public void repaint(Rectangle r, int pad) {
-               repaint(r.x - pad, r.y - pad, r.width + pad*2, r.height + pad*2);
-       }
-
-       public void repaint(AltosUIMapRectangle rect, int pad) {
-               repaint (transform.screen(rect), pad);
-       }
-
-       private boolean far_from_centre(AltosUILatLon lat_lon) {
-
-               if (centre == null || transform == null)
-                       return true;
-
-               Point2D.Double  screen = transform.screen(lat_lon);
-
-               int             width = getWidth();
-               int             dx = Math.abs ((int) screen.x - width/2);
-
-               if (dx > width / 4)
-                       return true;
-
-               int             height = getHeight();
-               int             dy = Math.abs ((int) screen.y - height/2);
-
-               if (dy > height / 4)
-                       return true;
-
-               return false;
-       }
-
-       public void show(AltosState state, AltosListenerState listener_state) {
-
-               /* If insufficient gps data, nothing to update
-                */
-               AltosGPS        gps = state.gps;
-
-               if (gps == null)
-                       return;
-
-               if (!gps.locked && gps.nsat < 4)
-                       return;
-
-               AltosUIMapRectangle     damage = path.add(gps.lat, gps.lon, state.state);
-
-               switch (state.state) {
-               case AltosLib.ao_flight_boost:
-                       if (!have_boost) {
-                               add_mark(gps.lat, gps.lon, state.state);
-                               have_boost = true;
-                       }
-                       break;
-               case AltosLib.ao_flight_landed:
-                       if (!have_landed) {
-                               add_mark(gps.lat, gps.lon, state.state);
-                               have_landed = true;
-                       }
-                       break;
-               }
-
-               if (damage != null)
-                       repaint(damage, AltosUIMapPath.stroke_width);
-               maybe_centre(gps.lat, gps.lon);
-       }
-
-       private void set_transform() {
-               Rectangle       bounds = getBounds();
-
-               transform = new AltosUIMapTransform(bounds.width, bounds.height, zoom, centre);
-               repaint();
-       }
-
-       public boolean set_zoom(int zoom) {
-               if (min_zoom <= zoom && zoom <= max_zoom && zoom != this.zoom) {
-                       this.zoom = zoom;
-                       tiles.clear();
-                       set_transform();
-
-                       for (AltosUIMapZoomListener listener : zoom_listeners)
-                               listener.zoom_changed(this.zoom);
-
-                       return true;
-               }
-               return false;
-       }
-
-       public void add_zoom_listener(AltosUIMapZoomListener listener) {
-               if (!zoom_listeners.contains(listener))
-                       zoom_listeners.add(listener);
-       }
-
-       public void remove_zoom_listener(AltosUIMapZoomListener listener) {
-               zoom_listeners.remove(listener);
-       }
-
-       public void set_load_params(double lat, double lon, int radius, AltosUIMapTileListener listener) {
-               load_centre = new AltosUILatLon(lat, lon);
-               load_radius = radius;
-               load_listener = listener;
-               centre(lat, lon);
-               make_tiles();
-               for (AltosUIMapTile tile : tiles.values()) {
-                       tile.add_store_listener(this);
-                       if (tile.store_status() != AltosUIMapStore.loading)
-                               listener.notify_tile(tile, tile.store_status());
-               }
-               repaint();
-       }
-
-       public boolean all_fetched() {
-               for (AltosUIMapTile tile : tiles.values()) {
-                       if (tile.store_status() == AltosUIMapStore.loading)
-                               return false;
-               }
-               return true;
-       }
-
-       public boolean set_maptype(int maptype) {
-               if (maptype != this.maptype) {
-                       this.maptype = maptype;
-                       tiles.clear();
-                       repaint();
-                       return true;
-               }
-               return false;
-       }
-
-       public int get_maptype() {
-               return maptype;
-       }
-
-       public int zoom() {
-               return zoom;
-       }
-
-       public void centre(AltosUILatLon lat_lon) {
-               centre = lat_lon;
-               set_transform();
-       }
-
-       public void centre(double lat, double lon) {
-               centre(new AltosUILatLon(lat, lon));
-       }
-
-       public void maybe_centre(double lat, double lon) {
-               AltosUILatLon   lat_lon = new AltosUILatLon(lat, lon);
-               if (centre == null || (!recent_user_input() && far_from_centre(lat_lon)))
-                       centre(lat_lon);
-       }
-
-       private VolatileImage create_back_buffer() {
-               return getGraphicsConfiguration().createCompatibleVolatileImage(getWidth(), getHeight());
-       }
-
-       private Point floor(Point2D.Double point) {
-               return new Point ((int) Math.floor(point.x / px_size) * px_size,
-                                 (int) Math.floor(point.y / px_size) * px_size);
-       }
-
-       private Point ceil(Point2D.Double point) {
-               return new Point ((int) Math.ceil(point.x / px_size) * px_size,
-                                 (int) Math.ceil(point.y / px_size) * px_size);
-       }
-
-       private void make_tiles() {
-               Point   upper_left;
-               Point   lower_right;
-
-               if (load_centre != null) {
-                       Point centre = floor(transform.point(load_centre));
-
-                       upper_left = new Point(centre.x - load_radius * px_size,
-                                              centre.y - load_radius * px_size);
-                       lower_right = new Point(centre.x + load_radius * px_size,
-                                              centre.y + load_radius * px_size);
-               } else {
-                       upper_left = floor(transform.screen_point(new Point(0, 0)));
-                       lower_right = floor(transform.screen_point(new Point(getWidth(), getHeight())));
-               }
-               LinkedList<Point> to_remove = new LinkedList<Point>();
-
-               for (Point point : tiles.keySet()) {
-                       if (point.x < upper_left.x || lower_right.x < point.x ||
-                           point.y < upper_left.y || lower_right.y < point.y) {
-                               to_remove.add(point);
-                       }
-               }
-
-               for (Point point : to_remove)
-                       tiles.remove(point);
-
-               cache.set_cache_size((getWidth() / px_size + 2) * (getHeight() / px_size + 2));
-               for (int y = upper_left.y; y <= lower_right.y; y += px_size) {
-                       for (int x = upper_left.x; x <= lower_right.x; x += px_size) {
-                               Point point = new Point(x, y);
-
-                               if (!tiles.containsKey(point)) {
-                                       AltosUILatLon   ul = transform.lat_lon(new Point2D.Double(x, y));
-                                       AltosUILatLon   center = transform.lat_lon(new Point2D.Double(x + px_size/2, y + px_size/2));
-                                       AltosUIMapTile tile = new AltosUIMapTile(this, ul, center, zoom, maptype,
-                                                                                px_size, AltosUILib.value_font);
-                                       tiles.put(point, tile);
-                               }
-                       }
-               }
-       }
-
-       /* AltosUIMapTileListener methods */
-       public synchronized void notify_tile(AltosUIMapTile tile, int status) {
-               for (Point point : tiles.keySet()) {
-                       if (tile == tiles.get(point)) {
-                               Point   screen = transform.screen(point);
-                               repaint(screen.x, screen.y, px_size, px_size);
-                       }
-               }
-       }
-
-       public AltosUIMapCache cache() { return cache; }
-
-       /* AltosUIMapStoreListener methods */
-       public synchronized void notify_store(AltosUIMapStore store, int status) {
-               if (load_listener != null) {
-                       for (AltosUIMapTile tile : tiles.values())
-                               if (store.equals(tile.store))
-                                       load_listener.notify_tile(tile, status);
-               }
-       }
-
-       private void do_paint(Graphics g) {
-               Graphics2D      g2d = (Graphics2D) g;
-
-               make_tiles();
-
-               for (AltosUIMapTile tile : tiles.values())
-                       tile.paint(g2d, transform);
-
-               synchronized(marks) {
-                       for (AltosUIMapMark mark : marks)
-                               mark.paint(g2d, transform);
-               }
-
-               path.paint(g2d, transform);
-
-               line.paint(g2d, transform);
-       }
-
-       public void paint(Graphics g) {
-               VolatileImage   back_buffer = create_back_buffer();
-               do {
-                       GraphicsConfiguration gc = getGraphicsConfiguration();
-                       int code = back_buffer.validate(gc);
-                       if (code == VolatileImage.IMAGE_INCOMPATIBLE)
-                               back_buffer = create_back_buffer();
-
-                       Graphics g_back = back_buffer.getGraphics();
-                       g_back.setClip(g.getClip());
-                       do_paint(g_back);
-                       g_back.dispose();
-
-                       g.drawImage(back_buffer, 0, 0, this);
-               } while (back_buffer.contentsLost());
-               back_buffer.flush();
-       }
-
-       public void update(Graphics g) {
-               paint(g);
-       }
-
-       public void add_mark(double lat, double lon, int state) {
-               synchronized(marks) {
-                       marks.add(new AltosUIMapMark(lat, lon, state));
-               }
-               repaint();
-       }
-
-       public void clear_marks() {
-               synchronized(marks) {
-                       marks.clear();
-               }
-       }
-
-       public AltosUIMapView() {
-               centre(0, 0);
-
-               addComponentListener(this);
-               addMouseMotionListener(this);
-               addMouseListener(this);
-               addMouseWheelListener(this);
-               set_font();
-       }
-}
diff --git a/altosuilib/AltosUIMapZoomListener.java b/altosuilib/AltosUIMapZoomListener.java
deleted file mode 100644 (file)
index 9f74bac..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright © 2014 Keith Packard <keithp@keithp.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; version 2 of the License.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public 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.altosuilib_6;
-
-public interface AltosUIMapZoomListener {
-       abstract public void zoom_changed(int zoom);
-}
index 843ee939b9843e8e5021df1b91adbe00901479de..ec5aae7813740aeb2e0a650d54c085549b623b86 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.ArrayList;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
index 9760494c74f7bea6193611f8a70d7c650493d526..5602dc17b457b85d9f5f8dae02345694b2ffc3af 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.*;
 import java.awt.Component;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosUIPreferences extends AltosPreferences {
 
@@ -55,10 +55,6 @@ public class AltosUIPreferences extends AltosPreferences {
 
        public static int position = AltosUILib.position_top_left;
 
-       static LinkedList<AltosUIMapCacheListener> map_cache_listeners;
-
-       public static int map_cache = 9;
-
        public static void init() {
                AltosPreferences.init(new AltosUIPreferencesBackend());
 
@@ -75,9 +71,6 @@ public class AltosUIPreferences extends AltosPreferences {
 
                position = backend.getInt(positionPreference, AltosUILib.position_top_left);
                position_listeners = new LinkedList<AltosPositionListener>();
-
-               map_cache = backend.getInt(mapCachePreference, 9);
-               map_cache_listeners = new LinkedList<AltosUIMapCacheListener>();
        }
 
        static { init(); }
@@ -225,32 +218,4 @@ public class AltosUIPreferences extends AltosPreferences {
                        return position;
                }
        }
-
-       public static void register_map_cache_listener(AltosUIMapCacheListener l) {
-               synchronized(backend) {
-                       map_cache_listeners.add(l);
-               }
-       }
-
-       public static void unregister_map_cache_listener(AltosUIMapCacheListener l) {
-               synchronized (backend) {
-                       map_cache_listeners.remove(l);
-               }
-       }
-
-       public static void set_map_cache(int new_map_cache) {
-               synchronized(backend) {
-                       map_cache = new_map_cache;
-                       backend.putInt(mapCachePreference, map_cache);
-                       flush_preferences();
-                       for (AltosUIMapCacheListener l: map_cache_listeners)
-                               l.map_cache_changed(map_cache);
-               }
-       }
-
-       public static int map_cache() {
-               synchronized(backend) {
-                       return map_cache;
-               }
-       }
 }
index 91fe42ec8e3b6f08959b1d60c7abba86fba88274..ebdc8b7785c95d306c764fa03b8223c275f3e92e 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.File;
 import java.util.prefs.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 import javax.swing.filechooser.FileSystemView;
 
 public class AltosUIPreferencesBackend implements AltosPreferencesBackend {
index 0c783a89d46a6ad3b4089101296b0f2cc68a36db..a88988083fa15f5a06e82bb8e7d21d690a38610a 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class AltosUIRateList extends JComboBox<String> {
 
index 4cd5ccd12ad77a6ef973a65ac457e926cab4f0a0..d4421ac7e7e15e5b1a14e8dfef3bf37e755f8c15 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.io.*;
 import java.util.ArrayList;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
index 77eef56754f3661315f521dc2e3634b4a53f0ce8..edbc7fd69a8660b4ed65c096c8488942730b31f3 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.util.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 
 public class AltosUITelemetryList extends JComboBox<String> {
index f86e274f271ec3757d42901909318b8003e0cc18..b58cd1cc7c8488e7c19f0bd0a3591be62e582e03 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public abstract class AltosUIUnitsIndicator extends AltosUIIndicator {
 
@@ -36,6 +36,10 @@ public abstract class AltosUIUnitsIndicator extends AltosUIIndicator {
                return hide(value(state, i));
        }
 
+       public boolean hide(AltosState state, AltosListenerState listener_state, int i) {
+               return hide(state, i);
+       }
+
        public double value (AltosState state, AltosListenerState listener_state, int i) {
                return value(state, i);
        }
@@ -77,7 +81,7 @@ public abstract class AltosUIUnitsIndicator extends AltosUIIndicator {
                                v[i] = value(state, listener_state, i);
                        else
                                v[i] = AltosLib.MISSING;
-                       if (hide(state, i))
+                       if (hide(state, listener_state, i))
                                hide = true;
                }
 
diff --git a/altosuilib/AltosUIVersion.java.in b/altosuilib/AltosUIVersion.java.in
deleted file mode 100644 (file)
index beb62cb..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.altosuilib_6;
-
-public class AltosUIVersion {
-       public final static String version = "@VERSION@";
-
-       public final static String google_maps_api_key = @GOOGLEKEY@;
-
-       static boolean has_google_maps_api_key() {
-               return google_maps_api_key != null && google_maps_api_key.length() > 1;
-       }
-}
index 44ad2ea2d175bea91013dd44242045776eab98c7..12649730f54f0bd0ca1f6cce896b7e35ef7ceb86 100644 (file)
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public abstract class AltosUIVoltageIndicator extends AltosUIUnitsIndicator {
 
index e940493fd5183da19ac0186aa64d825f387625f2..74306523c35b20575fd25d98332ded22fee97523 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.util.*;
 import libaltosJNI.*;
index 867f661900cbd74a7968bc08441320d82666a160..71a05a23b8e7a38cb5afaa60c1ecc8029b3f9711 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import com.sun.speech.freetts.Voice;
 import com.sun.speech.freetts.VoiceManager;
index 5d9ce2d9c3230fb49c0a67141d079d6142c613ca..919289c7b511193ed89d8d532cb277049bd8aeff 100644 (file)
@@ -15,7 +15,7 @@
  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.awt.*;
 import java.awt.event.*;
index d51da91de03f636cd81f9539804bcdee0cb42f64..a00e4a12f1fdc78a0e9b5dc927a3993e7ad3b627 100644 (file)
@@ -12,8 +12,6 @@ altosuilib_JAVA = \
        GrabNDrag.java \
        AltosDevice.java \
        AltosDeviceDialog.java \
-       AltosFlightDisplay.java \
-       AltosFontListener.java \
        AltosPositionListener.java \
        AltosUIConfigure.java \
        AltosUIAxis.java \
@@ -31,7 +29,6 @@ altosuilib_JAVA = \
        AltosUIPreferencesBackend.java \
        AltosUIPreferences.java \
        AltosUISeries.java \
-       AltosUIVersion.java \
        AltosUSBDevice.java \
        AltosVoice.java \
        AltosDisplayThread.java \
@@ -60,23 +57,8 @@ altosuilib_JAVA = \
        AltosBTDeviceIterator.java \
        AltosBTManage.java \
        AltosBTKnown.java \
-       AltosUIMap.java \
-       AltosUIMapView.java \
-       AltosUIMapLine.java \
-       AltosUIMapMark.java \
-       AltosUIMapPath.java \
-       AltosUIMapTile.java \
-       AltosUIMapCache.java \
-       AltosUIMapCacheListener.java \
-       AltosUIMapImage.java \
-       AltosUIMapTransform.java \
-       AltosUIMapRectangle.java \
-       AltosUIMapZoomListener.java \
-       AltosUIMapTileListener.java \
-       AltosUIMapPreload.java \
-       AltosUIMapStore.java \
-       AltosUIMapStoreListener.java \
-       AltosUILatLon.java \
+       AltosUIMapNew.java \
+       AltosUIMapPreloadNew.java \
        AltosUIFlightTab.java \
        AltosUIIndicator.java \
        AltosUIUnitsIndicator.java \
@@ -84,6 +66,7 @@ altosuilib_JAVA = \
        AltosUIFreqList.java \
        AltosUITelemetryList.java \
        AltosUIRateList.java \
+       AltosUIImage.java \
        OSXAdapter.java
 
 JAR=altosuilib_$(ALTOSUILIB_VERSION).jar
index 28b00ce1f7d5a88834cdc888fafd5e213bc53348..5155b0fec59e11ba2296c17540cd5e575cf3eb4c 100755 (executable)
@@ -55,7 +55,7 @@ Copyright © 2003-2007 Apple, Inc., All Rights Reserved
 
 */
 
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
 
 import java.lang.reflect.*;
 import java.util.HashMap;
index 5d876e21e9e2ead8179ff20d23a0d98a74ecacd1..d3d9dc956f859729289f0e48b93d75c91071f303 100755 (executable)
@@ -10,42 +10,16 @@ case $# in
        ;;
 esac
 
-while true; do
-       echo 'C 1' > $dev
-
-       echo -n "Generating RF carrier. Please enter measured frequency [enter for done]: "
-
-       read FREQ
-
-       echo 'C 0' > $dev
-
+../ao-tools/ao-cal-freq/ao-cal-freq --dev=$dev
+case $? in
+    0)
        calline=`./get-radio-cal $dev`
-       CURRENT_CAL=`echo $calline | awk '{print $2}'`
+       CAL_VALUE=`echo $calline | awk '{print $2}'`
        CURRENT_FREQ=`echo $calline | awk '{print $4}'`
-       CAL_VALUE=$CURRENT_CAL
-
-       case "$FREQ" in
-       "")
-               echo $SERIAL","$CAL_VALUE >> cal_values
-               exit 0
-               ;;
-       *)
-               echo "Current radio calibration "$CURRENT_CAL
-               echo "Current radio frequency "$CURRENT_FREQ
-
-               CAL_VALUE=`nickle -e "floor($CURRENT_FREQ / $FREQ * $CURRENT_CAL + 0.5)"`
-
-               echo "Programming flash with cal value " $CAL_VALUE
-
-               dd if=$dev iflag=nonblock
-
-               cat << EOF > $dev
-c f $CAL_VALUE
-c w
-EOF
-
-               echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE
-               ;;
-       esac
-done
-
+       echo $SERIAL","$CAL_VALUE >> cal_values
+       exit 0
+       ;;
+    *)
+       exit 1
+       ;;
+esac
index 76f3effb63ae69e4a36dff927b3d1a8826162a14..2f0a7822335e8ea46fb2babdfb6bc568da593e39 100755 (executable)
@@ -12,7 +12,7 @@ echo "\t$PRODUCT v$VERSION powered from USB"
 echo
 
 ret=1
-ao-list | while read product serial dev; do
+../ao-tools/ao-list/ao-list | while read product serial dev; do
     case "$product" in
        "$PRODUCT-v$VERSION")
 
index 57a4d90d78fdacb76418b3fe9ce61279da9593e7..13407e8675f38f1dd5ac7efc8eda262a1f8b9a0b 100755 (executable)
@@ -61,6 +61,7 @@ ao-list | while read product serial dev; do
            echo""
 
            echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
+           echo "\007"
            ret=0
            ;;
     esac
index b313e16286dd1ee2c83977bdd4535ec031b6c855..1e75e72fb0c9a04be36f87f07fabeaf94a681a22 100755 (executable)
@@ -1,14 +1,14 @@
 #!/bin/sh
 
-if [ -x /usr/bin/ao-flash-stm ]; then
-       STMLOAD=/usr/bin/ao-flash-stm
+if [ -x ../ao-tools/ao-flash/ao-flash-stm ]; then
+       STMLOAD=../ao-tools/ao-flash/ao-flash-stm
 else
        echo "Can't find ao-flash-stm!  Aborting."
        exit 1
 fi
 
-if [ -x /usr/bin/ao-usbload ]; then
-       USBLOAD=/usr/bin/ao-usbload
+if [ -x ../ao-tools/ao-usbload/ao-usbload ]; then
+       USBLOAD=../ao-tools/ao-usbload/ao-usbload
 else
        echo "Can't find ao-usbload!  Aborting."
        exit 1
@@ -38,7 +38,7 @@ $USBLOAD --serial=$SERIAL $REPO/easymega-v$VERSION*.elf || exit 1
 
 sleep 2
 
-dev=`ao-list | awk '/EasyMega-v'"$VERSION"'/ { print $3; exit(0); }'`
+dev=`../ao-tools/ao-list/ao-list | awk '/EasyMega-v'"$VERSION"'/ { print $3; exit(0); }'`
 
 case "$dev" in
 /dev/tty*)
@@ -52,7 +52,7 @@ esac
 
 echo 'E 0' > $dev
 
-../ao-tools/ao-cal-accel/ao-cal-accel $dev
+../ao-tools/ao-cal-accel/ao-cal-accel $dev || exit 1
 
 echo 'E 1' > $dev
 
index c4902f31ff68e5f938148cc9db6bddaf364f2bb9..f4100ae2f928f3546cb5b2f5419c534e91942784 100755 (executable)
@@ -1,8 +1,5 @@
 #!/bin/sh
 
-# serial number of the TeleDongle being used as the flash programmer
-DONGLE=612
-
 if [ -x ../ao-tools/ao-load/ao-load ]; then
        AOLOAD=../ao-tools/ao-load/ao-load
 elif [ -x /usr/bin/ao-load ]; then
@@ -34,6 +31,19 @@ read SERIAL
 
 echo $RAWLOAD
 
+case $USER in
+    bdale)
+       DONGLE=100
+       ;;
+    keithp)
+       DONGLE=186
+       ;;
+    *)
+       echo "Unknow user"
+       exit 1
+       ;;
+esac
+
 $RAWLOAD -D $DONGLE -r ao_led_blink.ihx
 echo "LEDs should be blinking"
 sleep 5
@@ -45,7 +55,7 @@ read FREQ
 CAL_VALUE=`nickle -e "floor(434.55 / $FREQ * 1186611 + 0.5)"`
 
 echo "Programming flash with cal value " $CAL_VALUE
-$AOLOAD -D $DONGLE --cal $CAL_VALUE /usr/share/altos/telebt-v1.0*.ihx $SERIAL
+$AOLOAD -D $DONGLE --cal $CAL_VALUE ~/altusmetrumllc/Binaries/telebt-v1.0*.ihx $SERIAL
 
 echo "Serial number "$SERIAL" programmed with RF cal value "$CAL_VALUE
 echo $SERIAL","$CAL_VALUE >> cal_values
index 123f0b548655ed086aab21eafcf6821edde6aef2..ba97d503c09d476c1e52068320c286977dbf1f0c 100755 (executable)
@@ -72,10 +72,8 @@ case "$dev" in
         ;;
 esac
 
-echo 'E 0' > $dev
+SERIAL=$SERIAL ./cal-freq $dev
 
 ./test-telegps
 
-SERIAL=$SERIAL ./cal-freq $dev
-
 exit $?
index 4450d6f6e57b2af10cfa5faded166cc2eac44310..6aef7f51b3635d741a269fa164c20e3024ea1509 100755 (executable)
@@ -18,11 +18,13 @@ else
        exit 1
 fi
 
-echo "TeleMini v1.0 Turn-On and Calibration Program"
+VERSION=1.0
+
+echo "TeleMini v$VERSION 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 "\tTeleMini v$VERSION 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
@@ -31,18 +33,27 @@ read SERIAL
 
 echo $RAWLOAD
 
-$RAWLOAD -D 100 -r ao_led_blink.ihx
+case $USER in
+    bdale)
+       programmer=100
+       ;;
+    keithp)
+       programmer=186
+       ;;
+esac
+
+$RAWLOAD -D $programmer -r ao_led_blink.ihx
 echo "LEDs should be blinking"
 sleep 5
 
-$RAWLOAD -D 100 -r ao_radio_xmit.ihx
+$RAWLOAD -D $programmer -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
+$AOLOAD -D $programmer --cal $CAL_VALUE ~/altusmetrumllc/Binaries/telemini-v$VERSION-*.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 6a170cbdc7e1c4e6861a85facac6a66c8cf70cf5..66d2560e0faa36b8ce09e8455fe235bf01362067 100644 (file)
@@ -2,7 +2,8 @@ SUBDIRS=lib ao-rawload ao-dbg ao-bitbang ao-eeprom ao-list \
        ao-load ao-telem ao-send-telem ao-sky-flash \
        ao-dumpflash ao-edit-telem ao-dump-up ao-elftohex \
        ao-flash ao-usbload ao-test-igniter ao-test-baro \
-       ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng
+       ao-test-flash ao-cal-accel ao-test-gps ao-usbtrng \
+       ao-cal-freq
 if LIBSTLINK
 SUBDIRS += ao-stmload
 endif
diff --git a/ao-tools/ao-cal-freq/.gitignore b/ao-tools/ao-cal-freq/.gitignore
new file mode 100644 (file)
index 0000000..4fe1486
--- /dev/null
@@ -0,0 +1 @@
+ao-cal-freq
diff --git a/ao-tools/ao-cal-freq/Makefile.am b/ao-tools/ao-cal-freq/Makefile.am
new file mode 100644 (file)
index 0000000..e11c2b0
--- /dev/null
@@ -0,0 +1,11 @@
+bin_PROGRAMS=ao-cal-freq
+
+AM_CFLAGS=-I$(top_srcdir)/ao-tools/lib $(LIBUSB_CFLAGS)
+
+ao_cal_freq_DEPENDENCIES = $(top_builddir)/ao-tools/lib/libao-tools.a
+
+ao_cal_freq_LDADD=$(top_builddir)/ao-tools/lib/libao-tools.a $(LIBUSB_LIBS) -lm
+
+ao_cal_freq_SOURCES=ao-cal-freq.c
+
+man_MANS = ao-cal-freq.1
diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.1 b/ao-tools/ao-cal-freq/ao-cal-freq.1
new file mode 100644 (file)
index 0000000..bfc3101
--- /dev/null
@@ -0,0 +1,58 @@
+.\"
+.\" 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-cal-freq" ""
+.SH NAME
+ao-cal-freq \- Calibrate AltOS flight computer frequency
+.SH SYNOPSIS
+.B "ao-cal-freq"
+[\-T \fItty-device\fP]
+[\--tty \fItty-device\fP]
+[\-D \fIaltos-device\fP]
+[\--device \fIaltos-device\fP]
+.SH DESCRIPTION
+.I ao-cal-freq
+drives the frequency calibration process and saves the result.
+.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
+TeleMega:2
+.br
+TeleMega
+.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 USAGE
+.I ao-cal-freq
+opens the target device, interactively calibrates the frequency, then
+shows the resulting calibration values and saves them to configuration
+memory.
+.SH AUTHOR
+Keith Packard
diff --git a/ao-tools/ao-cal-freq/ao-cal-freq.c b/ao-tools/ao-cal-freq/ao-cal-freq.c
new file mode 100644 (file)
index 0000000..464faf0
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 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 <stdbool.h>
+#include <termios.h>
+#include <math.h>
+#include "ao-elf.h"
+#include "ccdbg.h"
+#include "cc-usb.h"
+#include "cc.h"
+#include "ao-verbose.h"
+
+static const struct option options[] = {
+       { .name = "tty", .has_arg = 1, .val = 'T' },
+       { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "raw", .has_arg = 0, .val = 'r' },
+       { .name = "verbose", .has_arg = 1, .val = 'v' },
+       { 0, 0, 0, 0},
+};
+
+static void usage(char *program)
+{
+       fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>]\n", program);
+       exit(1);
+}
+
+void
+done(struct cc_usb *cc, int code)
+{
+       cc_usb_close(cc);
+       exit (code);
+}
+
+static int
+ends_with(char *whole, char *suffix)
+{
+       int whole_len = strlen(whole);
+       int suffix_len = strlen(suffix);
+
+       if (suffix_len > whole_len)
+               return 0;
+       return strcmp(whole + whole_len - suffix_len, suffix) == 0;
+}
+
+static int
+starts_with(char *whole, char *prefix)
+{
+       int whole_len = strlen(whole);
+       int prefix_len = strlen(prefix);
+
+       if (prefix_len > whole_len)
+               return 0;
+       return strncmp(whole, prefix, prefix_len) == 0;
+}
+
+static char **
+tok(char *line) {
+       char    **strs = malloc (sizeof (char *)), *str;
+       int     n = 0;
+
+       while ((str = strtok(line, " \t"))) {
+               line = NULL;
+               strs = realloc(strs, (n + 2) * sizeof (char *));
+               strs[n] = strdup(str);
+               n++;
+       }
+       strs[n] = '\0';
+       return strs;
+}
+
+static void
+free_strs(char **strs) {
+       char    *str;
+       int     i;
+
+       for (i = 0; (str = strs[i]) != NULL; i++)
+               free(str);
+       free(strs);
+}
+
+struct flash {
+       struct flash    *next;
+       char            line[512];
+       char            **strs;
+};
+
+static struct flash *
+flash(struct cc_usb *usb)
+{
+       struct flash    *head = NULL, **tail = &head;
+       cc_usb_printf(usb, "c s\nv\n");
+       for (;;) {
+               char    line[512];
+               struct flash    *b;
+
+               cc_usb_getline(usb, line, sizeof (line));
+               b = malloc (sizeof (struct flash));
+               strcpy(b->line, line);
+               b->strs = tok(line);
+               b->next = NULL;
+               *tail = b;
+               tail = &b->next;
+               if (strstr(line, "software-version"))
+                       break;
+       }
+       return head;
+}
+
+static void
+free_flash(struct flash *b) {
+       struct flash *n;
+
+       while (b) {
+               n = b->next;
+               free_strs(b->strs);
+               free(b);
+               b = n;
+       }
+}
+
+char **
+find_flash(struct flash *b, char *word0) {
+       int i;
+       for (;b; b = b->next) {
+               if (strstr(b->line, word0))
+                       return b->strs;
+       }
+       return NULL;
+}
+
+void
+await_key(void)
+{
+       struct termios  termios, termios_save;
+       char    buf[512];
+
+       tcgetattr(0, &termios);
+       termios_save = termios;
+       cfmakeraw(&termios);
+       tcsetattr(0, TCSAFLUSH, &termios);
+       read(0, buf, sizeof (buf));
+       tcsetattr(0, TCSAFLUSH, &termios_save);
+}
+
+int
+do_cal(struct cc_usb *usb) {
+       struct flash    *b;
+       char    line[1024];
+       double  measured_freq;
+       char    **cur_freq_words;
+       char    **cur_cal_words;
+       char    *line_end;
+       int     cur_freq;
+       int     cur_cal;
+       int     new_cal;
+
+       cc_usb_printf(usb, "E 0\n");
+
+       for(;;) {
+               cc_usb_printf(usb, "C 1\n");
+               cc_usb_sync(usb);
+
+               printf("Generating RF carrier. Please enter measured frequency [enter for done]: ");
+               fflush(stdout);
+               fgets(line, sizeof (line) - 1, stdin);
+               cc_usb_printf(usb, "C 0\n");
+               cc_usb_sync(usb);
+
+               measured_freq = strtod(line, &line_end);
+               if (line_end == line)
+                       break;
+
+               b = flash(usb);
+
+               cur_cal_words = find_flash(b, "Radio cal:");
+               cur_freq_words = find_flash(b, "Frequency:");
+
+               if (!cur_cal_words || !cur_freq_words) {
+                       printf("no response\n");
+                       return 0;
+               }
+
+               cur_cal = atoi(cur_cal_words[2]);
+               cur_freq = atoi(cur_freq_words[1]);
+
+               printf ("Current radio calibration %d\n", cur_cal);
+               printf ("Current radio frequency: %d\n", cur_freq);
+
+
+               new_cal = floor ((((double) cur_freq / 1000.0) / measured_freq) * cur_cal + 0.5);
+
+               printf ("Programming flash with cal value %d\n", new_cal);
+
+               cc_usb_printf (usb, "c f %d\nc w\n", new_cal);
+               cc_usb_sync(usb);
+       }
+       return 1;
+}
+
+int
+main (int argc, char **argv)
+{
+       char                    *device = NULL;
+       char                    *filename;
+       Elf                     *e;
+       unsigned int            s;
+       int                     i;
+       int                     c;
+       int                     tries;
+       struct cc_usb           *cc = NULL;
+       char                    *tty = NULL;
+       int                     success;
+       int                     verbose = 0;
+       int                     ret = 0;
+       int                     expected_size;
+
+       while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) {
+               switch (c) {
+               case 'T':
+                       tty = optarg;
+                       break;
+               case 'D':
+                       device = optarg;
+                       break;
+               case 'v':
+                       verbose++;
+                       break;
+               default:
+                       usage(argv[0]);
+                       break;
+               }
+       }
+
+       ao_verbose = verbose;
+
+       if (verbose > 1)
+               ccdbg_add_debug(CC_DEBUG_BITBANG);
+
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "AltosFlash");
+       if (!tty)
+               tty = cc_usbdevs_find_by_arg(device, "TeleMega");
+       if (!tty)
+               tty = getenv("ALTOS_TTY");
+       if (!tty)
+               tty="/dev/ttyACM0";
+
+       cc = cc_usb_open(tty);
+
+       if (!cc)
+               exit(1);
+
+       if (!do_cal(cc))
+               ret = 1;
+       done(cc, ret);
+}
index 6268dc8b75b9540bc69af66059ebbc2d76b3bfcb..b1f85af63473fe2485bf33780b7f79bd244efdb5 100644 (file)
 static const struct option options[] = {
        { .name = "tty", .has_arg = 1, .val = 'T' },
        { .name = "device", .has_arg = 1, .val = 'D' },
+       { .name = "wait", .has_arg = 0, .val = 'w' },
        { 0, 0, 0, 0},
 };
 
 static void usage(char *program)
 {
-       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>]\n", program);
+       fprintf(stderr, "usage: %s [--tty <tty-name>] [--device <device-name>] [--wait]\n", program);
        exit(1);
 }
 
@@ -134,7 +135,7 @@ static int swap16(int i)
 static int find_header(struct cc_usb *cc)
 {
        for (;;) {
-               if (get_nonwhite(cc, 0) == 'M' && get_nonwhite(cc, 1000) == 'P')
+               if (get_nonwhite(cc, -1) == 'M' && get_nonwhite(cc, 1000) == 'P')
                        return 1;
        }
 }
@@ -165,9 +166,13 @@ main (int argc, char **argv)
        int             i;
        int             crc;
        int             current_crc;
+       int             wait = 0;
 
-       while ((c = getopt_long(argc, argv, "T:D:", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "wT:D:", options, NULL)) != -1) {
                switch (c) {
+               case 'w':
+                       wait = 1;
+                       break;
                case 'T':
                        tty = optarg;
                        break;
@@ -179,8 +184,21 @@ main (int argc, char **argv)
                        break;
                }
        }
-       if (!tty)
-               tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART");
+       if (!tty) {
+               for (;;) {
+                       tty = cc_usbdevs_find_by_arg(device, "FT230X Basic UART");
+                       if (tty) {
+                               if (wait) {
+                                       printf("tty is %s\n", tty);
+                                       sleep(1);
+                               }
+                               break;
+                       }
+                       if (!wait)
+                               break;
+                       sleep(1);
+               }
+       }
        if (!tty)
                tty = getenv("ALTOS_TTY");
        if (!tty)
index 6b7ea6bb18c02fac3876757079a3b02d5b622dcd..4231dc2133d1dd069790ef7cc1df25f74b305198 100644 (file)
@@ -1,3 +1,3 @@
-bin_SCRIPTS=ao-flash-stm ao-flash-lpc
+bin_SCRIPTS=ao-flash-stm ao-flash-lpc ao-flash-stm32f0x
 
-man_MANS = ao-flash-stm.1 ao-flash-lpc.1
\ No newline at end of file
+man_MANS = ao-flash-stm.1 ao-flash-lpc.1 ao-flash-stm32f0x.1
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/ao-tools/ao-flash/ao-flash-stm32f0x b/ao-tools/ao-flash/ao-flash-stm32f0x
new file mode 100755 (executable)
index 0000000..45643a4
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+case "$#" in
+0)
+       echo "usage: $0 <filename> ..."
+       exit 1
+       ;;
+esac
+cmds=/tmp/flash$$
+trap "rm $cmds" 0 1 15
+file="$1"
+echo "program $file verify reset" > $cmds
+openocd \
+       -f interface/stlink-v2.cfg \
+       -f target/stm32f0x_stlink.cfg \
+       -f $cmds \
+       -c shutdown
diff --git a/ao-tools/ao-flash/ao-flash-stm32f0x.1 b/ao-tools/ao-flash/ao-flash-stm32f0x.1
new file mode 100644 (file)
index 0000000..07ff5b5
--- /dev/null
@@ -0,0 +1,36 @@
+.\"
+.\" Copyright © 2013 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; 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-FLASH-LPC 1 "ao-flash-stm32f0x" ""
+.SH NAME
+ao-flash-stm32f0x \- flash a program to a STM32F0x-based AltOS device using openocd
+.SH SYNOPSIS
+.B "ao-flash-stm32f0x"
+\fIfile.elf\fP
+.SH DESCRIPTION
+.I ao-flash-stm32f0x
+loads the specified .elf file into the target device flash memory.
+.SH USAGE
+.I ao-flash-stm32f0x
+is a simple script that passes the correct arguments to openocd to
+load a file into the target device via a connected STlink
+debugging dongle.
+.SH "SEE ALSO"
+openocd(1)
+.SH AUTHOR
+Keith Packard
index c4b43d8fb1caf6416e5f13dad9a33ad2b2ca8b40..4c065e797f992fc5b5a254f971ea5440498f2fdd 100644 (file)
@@ -28,12 +28,12 @@ main (int argc, char **argv)
        struct cc_usbdev        *dev;
        int                     i;
 
-       devs = cc_usbdevs_scan();
+       devs = cc_usbdevs_scan(TRUE);
        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);
+                               dev->product, dev->serial, dev->tty ? dev->tty : "(none)");
                }
                cc_usbdevs_free(devs);
        }
index 523229e6787bc0f40f1730957c4aa8d4f7a3a90d..cfb58bb433ce13022b4f3e681cfdb5f1c022ae80 100644 (file)
@@ -85,7 +85,7 @@ main (int argc, char **argv)
 
                        if (cc_mega_parse(line, &log)) {
                                if (log.is_config) {
-                                       printf ("kind %d\n", log.u.config_int.kind);
+                                       printf ("config %2d %s", log.u.config_int.kind, line);
                                } else {
                                        printf ("tick %5d ", log.tick);
                                        switch (log.type) {
@@ -126,8 +126,7 @@ main (int argc, char **argv)
                                                        printf (" s%d %6d",
                                                                j, log.u.volt.sense[j]);
                                                }
-                                               printf ("pyro %04x\n", log.u.volt.pyro);
-                                               printf ("\n");
+                                               printf (" pyro %04x\n", log.u.volt.pyro);
                                                break;
                                        default:
                                                printf ("type %c\n", log.type, log.tick);
index 7a1311b9c8583cb7edd59e73ae6edfa9c3fd3d3b..eabdd8a1d45b57c9b2435e56b683db8248c73ec9 100644 (file)
 .\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 .\"
 .\"
-.TH AO-LOAD 1 "ao-usbtrng" ""
+.TH AO-USBTRNG 1 "ao-usbtrng" ""
 .SH NAME
 ao-usbtrng \- dump random numbers from USBtrng
 .SH SYNOPSIS
-.B "ao-usbtrng"
-[\-T \fItty-device\fP]
-[\--tty \fItty-device\fP]
-[\-D \fIaltos-device\fP]
-[\--device \fIaltos-device\fP]
-\fIkbytes\fP
+.B "ao-usbtrng" [OPTION...] [KBYTES]
 .SH DESCRIPTION
 .I ao-usbtrng
-dumps random numbers from a USBtrng device
+dumps random numbers from a USBtrng device. If provided KBYTES specifies the number of 1024 byte blocks to produce on standard output. Without KBYTES
+.I ao-usbtrng
+produces random bytes continuously until killed.
 .SH OPTIONS
 .TP
-\-T tty-device | --tty tty-device
+\-v, --verbose
+increase verbosity
+.TP
+\-T, -tty=TTYDEVICE
 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
+\-D, --device=ALTOSDEVICE
 Search for a connected device. This requires an argument of one of the
 following forms:
 .IP
@@ -52,7 +52,7 @@ product, leaving out the serial number will cause the tool to match
 one of the available devices.
 .SH USAGE
 .I ao-usbtrng
-opens the target device and reads the specified number of kbytes of
+opens the target device and reads the specified number of KBYTES of
 random data.
 .SH AUTHOR
 Keith Packard
index 232f4e3e6a345a61e8ae1d3130cd8e92eff88a40..456885d9b4871b205d3d5cad13e8f7c2921fb7ec 100644 (file)
 static const struct option options[] = {
        { .name = "tty", .has_arg = 1, .val = 'T' },
        { .name = "device", .has_arg = 1, .val = 'D' },
-       { .name = "raw", .has_arg = 0, .val = 'r' },
-       { .name = "verbose", .has_arg = 1, .val = 'v' },
+       { .name = "verbose", .has_arg = 0, .val = 'v' },
        { 0, 0, 0, 0},
 };
 
 static void usage(char *program)
 {
-       fprintf(stderr, "usage: %s [--verbose=<verbose>] [--device=<device>] [-tty=<tty>] <kbytes>\n", program);
+       fprintf(stderr, "usage: %s [--verbose] [--device=<AltOS-device>] [-tty=<tty>] [<kbytes>]\n", program);
        exit(1);
 }
 
@@ -58,21 +57,17 @@ main (int argc, char **argv)
 {
        char                    *device = NULL;
        char                    *filename;
-       Elf                     *e;
-       unsigned int            s;
        int                     i;
        int                     c;
-       int                     tries;
        struct cc_usb           *cc = NULL;
        char                    *tty = NULL;
-       int                     success;
        int                     verbose = 0;
        int                     ret = 0;
-       int                     expected_size;
-       int                     kbytes;
+       int                     kbytes = 0; /* 0 == continuous */
+       int                     written;
        uint8_t                 bits[1024];
 
-       while ((c = getopt_long(argc, argv, "rT:D:c:s:v:", options, NULL)) != -1) {
+       while ((c = getopt_long(argc, argv, "vT:D:", options, NULL)) != -1) {
                switch (c) {
                case 'T':
                        tty = optarg;
@@ -89,12 +84,8 @@ main (int argc, char **argv)
                }
        }
 
-       if (!argv[optind])
-               usage(argv[0]);
-
-       kbytes = atoi(argv[optind]);
-       if (kbytes < 1)
-               kbytes = 1;
+       if (optind < argc)
+               kbytes = atoi(argv[optind]);
 
        ao_verbose = verbose;
 
@@ -113,13 +104,22 @@ main (int argc, char **argv)
        if (!cc)
                exit(1);
 
-       cc_usb_printf(cc, "f %d\n", kbytes);
+       if (kbytes) {
+               cc_usb_printf(cc, "f %d\n", kbytes);
 
-       while (kbytes--) {
-               int     i;
-               for (i = 0; i < 1024; i++)
-                       bits[i] = cc_usb_getchar(cc);
-               write(1, bits, 1024);
+               while (kbytes--) {
+                       for (i = 0; i < 1024; i++)
+                               bits[i] = cc_usb_getchar(cc);
+                       write(1, bits, 1024);
+               }
+       } else { /* 0 == continuous */
+               written = 0;
+               while (written >= 0) {
+                       cc_usb_printf(cc, "f 1\n");
+                       for (i = 0; i < 1024; i++)
+                               bits[i] = cc_usb_getchar(cc);
+                       written = write(1, bits, 1024);
+               }
        }
 
        done(cc, ret);
index 1a4dc7a1163f7460bdf139422be70d72ed44d35c..1e023c7ebe8f7b37e99eeef7b044d294a5f531dd 100644 (file)
@@ -207,8 +207,10 @@ _cc_usb_sync(struct cc_usb *cc, int wait_for_input, int write_timeout)
                                        write(2, cc->in_buf, cc->in_count);
                                        cc->in_count = 0;
                                }
-                       } else if (ret < 0)
+                       } else if (ret <= 0) {
                                perror("read");
+                               return -1;
+                       }
                }
                if (fds.revents & POLLOUT) {
                        ret = write(cc->fd, cc->out_buf,
index 95bfa244b6a5f9dfe0fa567b992d02221e47f149..6c3ba5910b3602465e918dc38f7bcec04d700e9f 100644 (file)
@@ -219,7 +219,7 @@ is_am(int idVendor, int idProduct) {
 }
 
 struct cc_usbdevs *
-cc_usbdevs_scan(void)
+cc_usbdevs_scan(int non_tty)
 {
        int                     e;
        struct dirent           **ents;
@@ -241,7 +241,7 @@ cc_usbdevs_scan(void)
                dir = cc_fullname(USB_DEVICES, ents[e]->d_name);
                dev = usb_scan_device(dir);
                free(dir);
-               if (is_am(dev->idVendor, dev->idProduct) && dev->tty) {
+               if (is_am(dev->idVendor, dev->idProduct) && (non_tty || dev->tty)) {
                        if (devs->dev)
                                devs->dev = realloc(devs->dev,
                                                    (devs->ndev + 1) * sizeof (struct usbdev *));
@@ -274,7 +274,7 @@ match_dev(char *product, int serial)
        int                     i;
        char                    *tty = NULL;
 
-       devs = cc_usbdevs_scan();
+       devs = cc_usbdevs_scan(FALSE);
        if (!devs)
                return NULL;
        for (i = 0; i < devs->ndev; i++) {
index bff4b2c9992cea601d785cfa08dbac6fc99e44e0..ff95e4fc9ad171e30d9e5b179960c65843dbef3b 100644 (file)
@@ -50,7 +50,7 @@ void
 cc_usbdevs_free(struct cc_usbdevs *usbdevs);
 
 struct cc_usbdevs *
-cc_usbdevs_scan(void);
+cc_usbdevs_scan(int non_tty);
 
 char *
 cc_usbdevs_find_by_arg(char *arg, char *default_product);
index b09daa823ed1528c5509bd8454f95d653c532678..bbf82377e9a1843db3bfaccf35ddd1883e5da910 100644 (file)
@@ -18,19 +18,21 @@ dnl
 dnl Process this file with autoconf to create configure.
 
 AC_PREREQ(2.57)
-AC_INIT([altos], 1.6)
+AC_INIT([altos], 1.6.1)
+ANDROID_VERSION=9
 AC_CONFIG_SRCDIR([src/kernel/ao.h])
 AM_INIT_AUTOMAKE([foreign dist-bzip2])
 AM_MAINTAINER_MODE
 
 VERSION_DASH=`echo $VERSION | sed 's/\./-/g'`
 AC_SUBST(VERSION_DASH)
+AC_SUBST(ANDROID_VERSION)
 
 dnl ==========================================================================
 dnl Java library versions
 
-ALTOSUILIB_VERSION=6
-ALTOSLIB_VERSION=6
+ALTOSUILIB_VERSION=8
+ALTOSLIB_VERSION=8
 
 AC_SUBST(ALTOSLIB_VERSION)
 AC_DEFINE(ALTOSLIB_VERSION,$ALTOSLIB_VERSION,[Version of the AltosLib package])
@@ -514,9 +516,9 @@ AC_OUTPUT([
 Makefile
 src/Makedefs
 altoslib/Makefile
+altoslib/AltosVersion.java
 icon/Makefile
 altosuilib/Makefile
-altosuilib/AltosUIVersion.java
 altosui/Makefile
 altosui/Info.plist
 altosui/altos-windows.nsi
@@ -529,6 +531,7 @@ telegps/Info.plist
 telegps/telegps-windows.nsi
 altosdroid/Makefile
 altosdroid/local.properties
+altosdroid/AndroidManifest.xml
 ao-tools/Makefile
 ao-tools/lib/Makefile
 ao-tools/ao-rawload/Makefile
@@ -551,6 +554,7 @@ ao-tools/ao-test-igniter/Makefile
 ao-tools/ao-test-baro/Makefile
 ao-tools/ao-test-flash/Makefile
 ao-tools/ao-cal-accel/Makefile
+ao-tools/ao-cal-freq/Makefile
 ao-tools/ao-test-gps/Makefile
 ao-tools/ao-usbtrng/Makefile
 ao-utils/Makefile
index f3a37ed0aa21352b118d3659508f25ae49b605a1..c9b0fa83ec6f5e064a835cc70bf2360f81d7ba68 100644 (file)
@@ -6,7 +6,7 @@ Uploaders: Keith Packard <keithp@keithp.com>
 Build-Depends: debhelper (>= 7), autoconf, automake, gawk, libreadline-dev, libusb-1.0-0-dev, nickle, cc1111, xsltproc, fop, xmlto, docbook-xml, docbook-xsl, swig, default-jdk, freetts, libtool, libjfreechart-java, libbluetooth-dev, pkg-config, libelf-dev, libbluetooth-dev, libssl-dev, gcc-arm-none-eabi, icoutils, librsvg2-bin, icnsutils, graphicsmagick | imagemagick, netpbm, shared-mime-info, libgtk-3-bin
 Standards-Version: 3.9.5
 Homepage: http://altusmetrum.org/AltOS
-Vcs-Git: git://git.gag.com/fw/altos
+Vcs-Git: git://git.gag.com/fw/altos -b debian
 Vcs-Browser: http://git.gag.com/?p=fw/altos
 
 Package: altos
index 2887a229465242fa1ccd094f9499ec66e2c6fc61..9c6189b4f9386a8c138e0d2033da7b8a8fbf715c 100644 (file)
@@ -18,7 +18,8 @@ RELNOTES=\
        release-notes-1.4.html \
        release-notes-1.4.1.html \
        release-notes-1.5.html \
-       release-notes-1.6.html
+       release-notes-1.6.html \
+       release-notes-1.6.1.html
 
 PICTURES=\
        altosui.png \
@@ -51,6 +52,7 @@ PICTURES=\
        micropeak-statistics.png \
        MicroPeakUSB-2.0-inuse.jpg \
        MicroPeakUSB-2.0.jpg \
+       monitor-idle.png \
        scan-channels.png \
        site-map.png \
        table.png \
index 1631983a93a6caa9edf8fe0b4cd8e8d80e2a7d03..a40481f16c83b2253194f9f6d118ed1ad67277f1 100644 (file)
       </para>
     </legalnotice>
     <revhistory>
+      <revision>
+       <revnumber>1.6.1</revnumber>
+       <date>15 July 2015</date>
+       <revremark>
+         Minor release adding TeleBT v3.0 support.
+       </revremark>
+      </revision>
       <revision>
        <revnumber>1.6</revnumber>
        <date>8 January 2015</date>
@@ -2620,9 +2627,9 @@ NAR #88757, TRA #12200
          <listitem>
            <para>
              After Motor. The flight software counts each time the
-             rocket starts accelerating (presumably due to a motor or
-             motors igniting). Use this value to count ignitions for
-             multi-staged or multi-airstart launches.
+             rocket starts accelerating and then decelerating
+             (presumably due to a motor or motors burning). Use this
+             value for multi-staged or multi-airstart launches.
            </para>
          </listitem>
          <listitem>
@@ -3853,8 +3860,7 @@ NAR #88757, TRA #12200
       <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.
+       connectivity at the site.
       </para>
       <para>
        There's a drop-down menu of launch sites we know about; if
@@ -3909,15 +3915,18 @@ NAR #88757, TRA #12200
        You can specify the range of zoom levels to download; smaller
        numbers show more area with less resolution. The default
        level, 0, shows about 3m/pixel. One zoom level change
-       doubles or halves that number.
+       doubles or halves that number. Larger zoom levels show more
+       detail, smaller zoom levels less.
       </para>
       <para>
-       The Tile Radius value sets how large an area around the center
-       point to download. Each tile is 512x512 pixels, and the
-       'radius' value specifies how many tiles away from the center
-       will be downloaded. Specify a radius of 0 and you get only the
-       center tile. A radius of 1 loads a 3x3 grid, centered on the
-       specified location.
+       The Map Radius value sets how large an area around the center
+       point to download. Select a value large enough to cover any
+       plausible flight from that site. Be aware that loading a large
+       area with a high maximum zoom level can attempt to download a
+       lot of data. Loading hybrid maps with a 10km radius at a
+       minimum zoom of -2 and a maximum zoom of 2 consumes about
+       120MB of space. Terrain and road maps consume about 1/10 as
+       much space as satellite or hybrid maps.
       </para>
       <para>
        Clicking the 'Load Map' button will fetch images from Google
@@ -3929,6 +3938,13 @@ NAR #88757, TRA #12200
     </section>
     <section>
       <title>Monitor Idle</title>
+      <informalfigure>
+       <mediaobject>
+         <imageobject>
+           <imagedata fileref="monitor-idle.png" width="5.2in" scalefit="1"/>
+         </imageobject>
+       </mediaobject>
+      </informalfigure>
       <para>
        This brings up a dialog similar to the Monitor Flight UI,
        except it works with the altimeter in “idle” mode by sending
@@ -3939,22 +3955,28 @@ NAR #88757, TRA #12200
        cannot manage to run Monitor Idle, then it's very likely that
        your callsigns are different in some way.
       </para>
+      <para>
+       You can change the frequency and callsign used to communicate
+       with the flight computer; they must both match the
+       configuration in the flight computer exactly.
+      </para>
     </section>
   </chapter>
   <chapter>
     <title>AltosDroid</title>
     <para>
       AltosDroid provides the same flight monitoring capabilities as
-      AltosUI, but runs on Android devices and is designed to connect
-      to a TeleBT receiver over Bluetooth™. AltosDroid monitors
+      AltosUI, but runs on Android devices. AltosDroid is designed to connect
+      to a TeleBT receiver over Bluetooth™ and (on Android devices supporting
+      USB On-the-go) TeleDongle and TeleBT devices over USB. AltosDroid monitors
       telemetry data, logging it to internal storage in the Android
-      device, and presents that data in a UI the same way the 'Monitor
-      Flight' window does in AltosUI.
+      device, and presents that data in a UI similar to the 'Monitor
+      Flight' window in AltosUI.
     </para>
     <para>
-      This manual will explain how to configure AltosDroid, connect
-      to TeleBT, operate the flight monitoring interface and describe
-      what the displayed data means.
+      This manual will explain how to configure AltosDroid, connect to
+      TeleBT or TeleDongle, operate the flight monitoring interface
+      and describe what the displayed data means.
     </para>
     <section>
       <title>Installing AltosDroid</title>
@@ -3968,7 +3990,7 @@ NAR #88757, TRA #12200
       </para>
     </section>
     <section>
-      <title>Connecting to TeleBT</title>
+      <title>Connecting to TeleBT over Bluetooth™</title>
       <para>
        Press the Android 'Menu' button or soft-key to see the
        configuration options available. Select the 'Connect a device'
@@ -3982,15 +4004,91 @@ NAR #88757, TRA #12200
        scanning.
       </para>
     </section>
+    <section>
+      <title>Connecting to TeleDongle or TeleBT over USB</title>
+      <para>
+       Get a special USB On-the-go adapter cable. These cables have a USB
+       micro-B male connector on one end and a standard A female
+       connector on the other end. Plug in your TeleDongle or TeleBT
+       device to the adapter cable and the adapter cable into your
+       phone and AltosDroid should automatically start up. If it
+       doesn't, the most likely reason is that your Android device
+       doesn't support USB On-the-go.
+      </para>
+    </section>
     <section>
       <title>Configuring AltosDroid</title>
       <para>
-       The only configuration option available for AltosDroid is
-       which frequency to listen on. Press the Android 'Menu' button
-       or soft-key and pick the 'Select radio frequency' entry. That
-       brings up a menu of pre-set radio frequencies; pick the one
-       which matches your altimeter.
+       There are several configuration and operation parameters
+       available in the AltosDroid menu.
       </para>
+      <section>
+       <title>Select radio frequency</title>
+       <para>
+         This selects which frequency to listen on by bringing up a
+         menu of pre-set radio frequencies. Pick the one which matches
+         your altimeter.
+       </para>
+      </section>
+      <section>
+       <title>Select data rate</title>
+       <para>
+         Altus Metrum transmitters can be configured to operate at
+         lower data rates to improve transmission range. If you have
+         configured your device to do this, this menu item allows you
+         to change the receiver to match.
+       </para>
+      </section>
+      <section>
+       <title>Change units</title>
+       <para>
+         This toggles between metric and imperial units.
+       </para>
+      </section>
+      <section>
+       <title>Load maps</title>
+       <para>
+         Brings up a dialog allowing you to download offline map
+         tiles so that you can have maps available even if you have
+         no network connectivity at the launch site.
+       </para>
+      </section>
+      <section>
+       <title>Map type</title>
+       <para>
+         Displays a menu of map types and lets you select one. Hybrid
+         maps include satellite images with a roadmap
+         overlaid. Satellite maps dispense with the roadmap
+         overlay. Roadmap shows just the roads. Terrain includes
+         roads along with shadows indicating changes in elevation,
+         and other geographical features.
+       </para>
+      </section>
+      <section>
+       <title>Toggle Online/Offline maps</title>
+       <para>
+         Switches between online and offline maps. Online maps will
+         show a 'move to current position' icon in the upper right
+         corner, while offline maps will have copyright information
+         all over the map. Otherwise, they're pretty similar.
+       </para>
+      </section>
+      <section>
+       <title>Select Tracker</title>
+       <para>
+         Switches the information displays to show data for a
+         different transmitting device. The map will always show all
+         of the devices in view. Trackers are shown and selected by
+         serial number, so make sure you note the serial number of
+         devices in each airframe.
+       </para>
+      </section>
+      <section>
+       <title>Delete Track</title>
+       <para>
+         Deletes all information about a transmitting device.
+       </para>
+      </section>
     </section>
     <section>
       <title>AltosDroid Flight Monitoring</title>
@@ -4004,91 +4102,353 @@ NAR #88757, TRA #12200
       <section>
        <title>Pad</title>
         <para>
-          The 'Launch Pad' tab shows information used to decide when the
+          The '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:
-          <variablelist>
-           <varlistentry>
-             <term>Battery Voltage</term>
-             <listitem>
-               <para>
-                 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.8V is required for a 'GO' status.
-               </para>
-             </listitem>
-           </varlistentry>
-           <varlistentry>
-             <term>Apogee Igniter Voltage</term>
-             <listitem>
-               <para>
-                 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>
-           </varlistentry>
-           <varlistentry>
-             <term>Main Igniter Voltage</term>
-             <listitem>
-               <para>
-                 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>
-           </varlistentry>
-           <varlistentry>
-             <term>On-board Data Logging</term>
-             <listitem>
-               <para>
-                 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>
-           </varlistentry>
-           <varlistentry>
-             <term>GPS Locked</term>
-             <listitem>
-               <para>
-                 For a TeleMetrum or TeleMega 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>
-           </varlistentry>
-           <varlistentry>
-             <term>GPS Ready</term>
-             <listitem>
-               <para>
-                 For a TeleMetrum or TeleMega 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>
-           </varlistentry>
-          </variablelist>
+          whether the rocket is ready to launch.
        </para>
        <para>
-         The Launchpad tab also shows the computed launch pad position
-         and altitude, averaging many reported positions to improve the
-         accuracy of the fix.
+         When the pad tab is selected, the voice responses will
+         include status changes to the igniters and GPS reception,
+         letting you know if the rocket is still ready for launch.
+       </para>
+        <variablelist>
+         <varlistentry>
+           <term>Battery</term>
+           <listitem>
+             <para>
+               This indicates whether the Li-Po battery
+               powering the transmitter has sufficient charge to last for
+               the duration of the flight. A value of more than
+               3.8V is required for a 'GO' status.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Receiver Battery</term>
+           <listitem>
+             <para>
+               This indicates whether the Li-Po battery
+               powering the TeleBT has sufficient charge to last for
+               the duration of the flight. A value of more than
+               3.8V is required for a 'GO' status.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Data Logging</term>
+           <listitem>
+             <para>
+               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 and TeleMega can store multiple
+               flights, depending on the configured maximum flight
+               log size. TeleGPS logs data continuously. 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>
+         </varlistentry>
+         <varlistentry>
+           <term>GPS Locked</term>
+           <listitem>
+             <para>
+               For a TeleMetrum or TeleMega 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>
+         </varlistentry>
+         <varlistentry>
+           <term>GPS Ready</term>
+           <listitem>
+             <para>
+               For a TeleMetrum or TeleMega 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>
+         </varlistentry>
+         <varlistentry>
+           <term>Apogee Igniter</term>
+           <listitem>
+             <para>
+               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>
+         </varlistentry>
+         <varlistentry>
+           <term>Main Igniter</term>
+           <listitem>
+             <para>
+               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>
+         </varlistentry>
+         <varlistentry>
+           <term>Igniter A-D</term>
+           <listitem>
+             <para>
+               This indicates whether the indicated additional pyro
+               channel 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>
+         </varlistentry>
+        </variablelist>
+       <para>
+         The Pad tab also shows the location of the Android device.
+       </para>
+      </section>
+      <section>
+       <title>Flight</title>
+        <para>
+          The 'Flight' tab shows information used to evaluate and spot
+          a rocket while in flight. It displays speed and height data
+          to monitor the health of the rocket, along with elevation,
+          range and bearing to help locate the rocket in the sky.
+       </para>
+       <para>
+         While the Flight tab is displayed, the voice announcements
+         will include current speed, height, elevation and bearing
+         information.
+       </para>
+        <variablelist>
+         <varlistentry>
+           <term>Speed</term>
+           <listitem>
+             <para>
+               Shows current vertical speed. During descent, the
+               speed values are averaged over a fairly long time to
+               try and make them steadier.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Height</term>
+           <listitem>
+             <para>
+               Shows the current height above the launch pad.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Max Speed</term>
+           <listitem>
+             <para>
+               Shows the maximum vertical speed seen during the flight.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Max Height</term>
+           <listitem>
+             <para>
+               Shows the maximum height above launch pad.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Elevation</term>
+           <listitem>
+             <para>
+               This is the angle above the horizon from the android
+               devices current position.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Range</term>
+           <listitem>
+             <para>
+               The total distance from the android device to the
+               rocket, including both ground distance and
+               difference in altitude. Use this to gauge how large
+               the rocket is likely to appear in the sky.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Bearing</term>
+           <listitem>
+             <para>
+               This is the aziumuth from true north for the rocket
+               from the android device. Use this in combination
+               with the Elevation value to help locate the rocket
+               in the sky, or at least to help point the antenna in
+               the general direction. This is provided in both
+               degrees and a compass point (like West South
+               West). You'll want to know which direction is true
+               north before launching your rocket.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Ground Distance</term>
+           <listitem>
+             <para>
+               This shows the distance across the ground to the
+               lat/lon where the rocket is located. Use this to
+               estimate what is currently under the rocket.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Latitude/Longitude</term>
+           <listitem>
+             <para>
+               Displays the last known location of the rocket.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Apogee Igniter</term>
+           <listitem>
+             <para>
+               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>
+         </varlistentry>
+         <varlistentry>
+           <term>Main Igniter</term>
+           <listitem>
+             <para>
+               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>
+         </varlistentry>
+        </variablelist>
+      </section>
+      <section>
+       <title>Recover</title>
+        <para>
+          The 'Recover' tab shows information used while recovering the
+         rocket on the ground after flight.
+       </para>
+       <para>
+         While the Recover tab is displayed, the voice announcements
+         will include distance along with either bearing or
+         direction, depending on whether you are moving.
+       </para>
+        <variablelist>
+         <varlistentry>
+           <term>Bearing</term>
+           <listitem>
+             <para>
+               This is the aziumuth from true north for the rocket
+               from the android device. Use this in combination
+               with the Elevation value to help locate the rocket
+               in the sky, or at least to help point the antenna in
+               the general direction. This is provided in both
+               degrees and a compass point (like West South
+               West). You'll want to know which direction is true
+               north before launching your rocket.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Direction</term>
+           <listitem>
+             <para>
+               When you are in motion, this provides the angle from
+               your current direction of motion towards the rocket.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Distance</term>
+           <listitem>
+             <para>
+               Distance over the ground to the rocket.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Tar Lat/Tar Lon</term>
+           <listitem>
+             <para>
+               Displays the last known location of the rocket.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>My Lat/My Lon</term>
+           <listitem>
+             <para>
+               Displays the location of the Android device.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Max Height</term>
+           <listitem>
+             <para>
+               Shows the maximum height above launch pad.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Max Speed</term>
+           <listitem>
+             <para>
+               Shows the maximum vertical speed seen during the flight.
+             </para>
+           </listitem>
+         </varlistentry>
+         <varlistentry>
+           <term>Max Accel</term>
+           <listitem>
+             <para>
+               Shows the maximum vertical acceleration seen during the flight.
+             </para>
+           </listitem>
+         </varlistentry>
+        </variablelist>
+      </section>
+      <section>
+       <title>Map</title>
+       <para>
+         The 'Map' tab shows a map of the area around the rocket
+         being tracked along with information needed to recover it.
+       </para>
+       <para>
+         On the map itself, icons showing the location of the android
+         device along with the last known location of each tracker. A
+         blue line is drawn from the android device location to the
+         currently selected tracker.
+       </para>
+       <para>
+         Below the map, the distance and either bearing or direction
+         along with the lat/lon of the target and the android device
+         are shown
+       </para>
+       <para>
+         The Map tab provides the same voice announcements as the
+         Recover tab.
        </para>
       </section>
     </section>
@@ -4097,9 +4457,9 @@ NAR #88757, TRA #12200
       <para>
        AltosDroid always saves every bit of telemetry data it
        receives. To download that to a computer for use with AltosUI,
-       simply remove the SD card from your Android device, or connect
-       your device to your computer's USB port and browse the files
-       on that device. You will find '.telem' files in the TeleMetrum
+       remove the SD card from your Android device, or connect your
+       device to your computer's USB port and browse the files on
+       that device. You will find '.telem' files in the TeleMetrum
        directory that will work with AltosUI directly.
       </para>
     </section>
@@ -5915,6 +6275,13 @@ NAR #88757, TRA #12200
   </appendix>
   <appendix>
     <title>Release Notes</title>
+    <simplesect>
+      <title>Version 1.6.1</title>
+      <xi:include
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         href="release-notes-1.6.1.xsl"
+         xpointer="xpointer(/article/*)"/>
+    </simplesect>
     <simplesect>
       <title>Version 1.6</title>
       <xi:include
index ae98c9a59a9c084c92b60b75e9763ae1c5f49600..150b8b3ca3c0a29a6f885a7c781439e1f0136d02 100644 (file)
Binary files a/doc/load-maps.png and b/doc/load-maps.png differ
diff --git a/doc/monitor-idle.png b/doc/monitor-idle.png
new file mode 100644 (file)
index 0000000..964063f
Binary files /dev/null and b/doc/monitor-idle.png differ
diff --git a/doc/release-notes-1.6.1.xsl b/doc/release-notes-1.6.1.xsl
new file mode 100644 (file)
index 0000000..058d43f
--- /dev/null
@@ -0,0 +1,189 @@
+<?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.6.1 includes support for our updated TeleBT v3.0
+    product and bug fixes in in the flight software for all our boards
+    and ground station interfaces.
+  </para>
+  <para>
+    AltOS New Features
+    <itemizedlist>
+      <listitem>
+       <para>
+         Add support for TeleBT v3.0 boards.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Add support for uncompressed APRS data, providing support
+         for older APRS receivers. Uncompressed APRS data is less
+         precise, takes more bandwidth and doesn't have integrated
+         altitude data.
+       </para>
+      </listitem>
+    </itemizedlist>
+  </para>
+  <para>
+    AltOS Fixes
+    <itemizedlist>
+      <listitem>
+       <para>
+         Make TeleDongle and TeleBT more tolerant of data rate
+         variations from transmitting devices.
+       </para>
+      </listitem>
+    </itemizedlist>
+  </para>
+  <para>
+    AltosUI and TeleGPS New Features
+    <itemizedlist>
+      <listitem>
+       <para>
+         Add map to Monitor Idle display. It's nice to be able to
+         verify that maps are working, instead of needing to use
+         Monitor Flight.
+       </para>
+      </listitem>
+    </itemizedlist>
+  </para>
+  <para>
+    AltosUI Fixes
+    <itemizedlist>
+      <listitem>
+       <para>
+         Fix frequency configuration to round values instead of
+         truncate them, avoiding a common 1kHz error in the setting.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Turn the Windows stub into a more useful program that can
+         launch the application with parameters so that file manager
+         icons work more reliably.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Force KML export to use a C locale so that numbers are
+         formatted with '.' instead of ',' for a decimal separator in
+         non-US locales. 
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Preload map tiles based on distance rather than number of
+         tiles; this means you get the same resolution covering the
+         entire area, rather than having high resolution near the
+         center and low resolution further away.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Allow configuration of frequency and callsign in Monitor
+         Idle mode.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Fix layout weirdness when resizing windows on
+         Windows. Windows shouldn't have giant blank spaces around
+         the useful content anymore.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Fix layout weirdness when resizing windows on
+         Windows. Windows shouldn't have giant blank spaces around
+         the useful content anymore.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Use a longer filter for descent speed values. This should
+         provide something more useful on the display, although it
+         will take longer to respond to changes now.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Make Replay Flight run in realtime again. It had been set to
+         run at 10x speed by mistake.
+       </para>
+      </listitem>
+    </itemizedlist>
+  </para>
+  <para>
+    AltosDroid New Features
+    <itemizedlist>
+      <listitem>
+       <para>
+         Add offline map support using mapping code from AltosUI.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Support TeleDongle (and TeleBT via USB) on devices
+         supporting USB On-The-Go.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Display additional TeleMega pyro channel status in Pad tab.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Switch between metric and imperial units.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Monitor TeleBT battery voltage.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Track multiple devices at the same time, selecting between
+         them with a menu or using the map.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Add hybrid, satellite and terrain map types.
+       </para>
+      </listitem>
+    </itemizedlist>
+  </para>
+  <para>
+    AltosDroid Fixes
+    <itemizedlist>
+      <listitem>
+       <para>
+         Use standard Android display conventions so that a menu
+         button is available in the application title bar.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Adjust layout to work on large and small screens; shrinking
+         the go/no-go lights in smaller environments to try and make
+         everything visible.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Make voice announcements depend on current tab.
+       </para>
+      </listitem>
+      <listitem>
+       <para>
+         Compute adjustment to current travel direction while in
+         motion towards rocket.
+       </para>
+      </listitem>
+    </itemizedlist>
+  </para>
+</article>
index e410150742b5caf9d6935e8b17de69885a3b2bfc..2e0b3ea1e9b22d8f452b125725749501cf1cf1fb 100644 (file)
       </para>
     </section>
     <section>
-      <title>Sensor Data</title>
+      <title>TeleMetrum v1.x, TeleMini and TeleNano Sensor Data</title>
       <informaltable frame='none' label='' tocentry='0'>
        <tgroup cols='2' align='center' colsep='1' rowsep='1'>
          <colspec align='center' colwidth='*' colname='Offset'/>
          <tbody>
            <row>
              <entry>0x01</entry>
-             <entry>TeleMetrum Sensor Data</entry>
+             <entry>TeleMetrum v1.x Sensor Data</entry>
            </row>
            <row>
              <entry>0x02</entry>
        </tgroup>
       </informaltable>
       <para>
-       TeleMetrum, TeleMini and TeleNano share this same packet
+       TeleMetrum v1.x, 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.
        </tgroup>
       </table>
     </section>
+    <section>
+      <title>TeleMega 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>0x08</entry>
+             <entry>TeleMega IMU Sensor Data</entry>
+           </row>
+           <row>
+             <entry>0x09</entry>
+             <entry>TeleMega Kalman and Voltage Data</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </informaltable>
+      <para>
+       TeleMega has a lot of sensors, and so it splits the sensor
+       data into two packets. The raw IMU data are sent more often;
+       the voltage values don't change very fast, and the Kalman
+       values can be reconstructed from the IMU data.
+      </para>
+      <para>
+       IMU 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>
+      <para>
+       Kalman and Voltage Data packets are transmitted once per second on the
+       ground, 5 times per second during ascent and once per second
+       during descent and landing
+      </para>
+      <para>
+       The high-g accelerometer is reported separately from the data
+       for the 9-axis IMU (accel/gyro/mag). The 9-axis IMU is mounted
+       so that the X axis is "across" the board (along the short
+       axis0, the Y axis is "along" the board (along the long axis,
+       with the high-g accelerometer) and the Z axis is "through" the
+       board (perpendicular to the board). Rotation measurements are
+       around the respective axis, so Y rotation measures the spin
+       rate of the rocket while X and Z rotation measure the tilt
+       rate.
+      </para>
+      <para>
+       The overall tilt angle of the rocket is computed by first
+       measuring the orientation of the rocket on the pad using the 3
+       axis accelerometer, and then integrating the overall tilt rate
+       from the 3 axis gyroscope to compute the total orientation
+       change of the airframe since liftoff.
+      </para>
+      <table frame='all'>
+       <title>TeleMega IMU 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>orient</entry><entry>Angle from vertical in degrees</entry>
+           </row>
+           <row>
+             <entry>6</entry><entry>int16_t</entry><entry>accel</entry><entry>High G accelerometer</entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>int32_t</entry><entry>pres</entry><entry>pressure (Pa * 10)</entry>
+           </row>
+           <row>
+             <entry>12</entry><entry>int16_t</entry><entry>temp</entry><entry>temperature (°C * 100)</entry>
+           </row>
+           <row>
+             <entry>14</entry><entry>int16_t</entry><entry>accel_x</entry><entry>X axis acceleration (across)</entry>
+           </row>
+           <row>
+             <entry>16</entry><entry>int16_t</entry><entry>accel_y</entry><entry>Y axis acceleration (along)</entry>
+           </row>
+           <row>
+             <entry>18</entry><entry>int16_t</entry><entry>accel_z</entry><entry>Z axis acceleration (through)</entry>
+           </row>
+           <row>
+             <entry>20</entry><entry>int16_t</entry><entry>gyro_x</entry><entry>X axis rotation (across)</entry>
+           </row>
+           <row>
+             <entry>22</entry><entry>int16_t</entry><entry>gyro_y</entry><entry>Y axis rotation (along)</entry>
+           </row>
+           <row>
+             <entry>24</entry><entry>int16_t</entry><entry>gyro_z</entry><entry>Z axis rotation (through)</entry>
+           </row>
+           <row>
+             <entry>26</entry><entry>int16_t</entry><entry>mag_x</entry><entry>X field strength (across)</entry>
+           </row>
+           <row>
+             <entry>28</entry><entry>int16_t</entry><entry>mag_y</entry><entry>Y field strength (along)</entry>
+           </row>
+           <row>
+             <entry>30</entry><entry>int16_t</entry><entry>mag_z</entry><entry>Z field strength (through)</entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <table frame='all'>
+       <title>TeleMega Kalman and Voltage Data 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>v_batt</entry><entry>battery voltage</entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>int16_t</entry><entry>v_pyro</entry><entry>pyro battery voltage</entry>
+           </row>
+           <row>
+             <entry>10</entry><entry>int8_t[6]</entry><entry>sense</entry><entry>pyro continuity sense</entry>
+           </row>
+           <row>
+             <entry>16</entry><entry>int32_t</entry><entry>ground_pres</entry><entry>Average barometer reading on ground</entry>
+           </row>
+           <row>
+             <entry>20</entry><entry>int16_t</entry><entry>ground_accel</entry><entry>Average accelerometer reading on ground</entry>
+           </row>
+           <row>
+             <entry>22</entry><entry>int16_t</entry><entry>accel_plus_g</entry><entry>Accel calibration at +1g</entry>
+           </row>
+           <row>
+             <entry>24</entry><entry>int16_t</entry><entry>accel_minus_g</entry><entry>Accel calibration at -1g</entry>
+           </row>
+           <row>
+             <entry>26</entry><entry>int16_t</entry><entry>acceleration</entry><entry>m/s² * 16</entry>
+           </row>
+           <row>
+             <entry>28</entry><entry>int16_t</entry><entry>speed</entry><entry>m/s * 16</entry>
+           </row>
+           <row>
+             <entry>30</entry><entry>int16_t</entry><entry>height</entry><entry>m</entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
+    <section>
+      <title>TeleMetrum v2 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>0x0A</entry>
+             <entry>TeleMetrum v2 Sensor Data</entry>
+           </row>
+           <row>
+             <entry>0x0B</entry>
+             <entry>TeleMetrum v2 Calibration Data</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </informaltable>
+      <para>
+       TeleMetrum v2 has higher resolution barometric data than
+       TeleMetrum v1, and so the constant calibration data is
+       split out into a separate packet.
+      </para>
+      <para>
+       TeleMetrum v2 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>
+      <para>
+       TeleMetrum v2 Calibration Data packets are always transmitted once per second.
+      </para>
+      <table frame='all'>
+       <title>TeleMetrum v2 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</entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>int32_t</entry><entry>pres</entry><entry>pressure sensor (Pa * 10)</entry>
+           </row>
+           <row>
+             <entry>12</entry><entry>int16_t</entry><entry>temp</entry><entry>temperature sensor (°C * 100)</entry>
+           </row>
+
+           <row>
+             <entry>14</entry><entry>int16_t</entry><entry>acceleration</entry><entry>m/s² * 16</entry>
+           </row>
+           <row>
+             <entry>16</entry><entry>int16_t</entry><entry>speed</entry><entry>m/s * 16</entry>
+           </row>
+           <row>
+             <entry>18</entry><entry>int16_t</entry><entry>height</entry><entry>m</entry>
+           </row>
+
+           <row>
+             <entry>20</entry><entry>int16_t</entry><entry>v_batt</entry><entry>battery voltage</entry>
+           </row>
+           <row>
+             <entry>22</entry><entry>int16_t</entry><entry>sense_d</entry><entry>drogue continuity sense</entry>
+           </row>
+           <row>
+             <entry>24</entry><entry>int16_t</entry><entry>sense_m</entry><entry>main continuity sense</entry>
+           </row>
+           <row>
+             <entry>26</entry><entry>pad[6]</entry><entry>pad bytes</entry><entry></entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+      <table frame='all'>
+       <title>TeleMetrum v2 Calibration Data 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>pad[3]</entry><entry>pad bytes</entry><entry></entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>int32_t</entry><entry>ground_pres</entry><entry>Average barometer reading on ground</entry>
+           </row>
+           <row>
+             <entry>12</entry><entry>int16_t</entry><entry>ground_accel</entry><entry>Average accelerometer reading on ground</entry>
+           </row>
+           <row>
+             <entry>14</entry><entry>int16_t</entry><entry>accel_plus_g</entry><entry>Accel calibration at +1g</entry>
+           </row>
+           <row>
+             <entry>16</entry><entry>int16_t</entry><entry>accel_minus_g</entry><entry>Accel calibration at -1g</entry>
+           </row>
+           <row>
+             <entry>18</entry><entry>pad[14]</entry><entry>pad bytes</entry><entry></entry>
+           </row>
+           <row>
+             <entry>32</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </table>
+    </section>
     <section>
       <title>Configuration Data</title>
       <informaltable frame='none' label='' tocentry='0'>
       </informaltable>
       <para>
        This packet provides all of the information available from the
-       Venus SkyTraq GPS receiver—position, time, speed and precision
+       GPS receiver—position, time, speed and precision
        estimates. 
       </para>
       <para>
        </tgroup>
       </table>
     </section>
+    <section>
+      <title>Companion Data 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>0x07</entry>
+             <entry>Companion Data Data</entry>
+           </row>
+         </tbody>
+       </tgroup>
+      </informaltable>
+      <para>
+       When a companion board is attached to TeleMega or TeleMetrum,
+       it can provide telemetry data to be included in the
+       downlink. The companion board can provide up to 12 16-bit data
+       values.
+      </para>
+      <para>
+       The companion board itself specifies the transmission rate. On
+       the ground and during descent, that rate is limited to one
+       packet per second. During ascent, that rate is limited to 10
+       packets per second.
+      </para>
+      <table frame='all'>
+       <title>Companion 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>board_id</entry>
+             <entry>Type of companion board attached</entry>
+           </row>
+           <row>
+             <entry>6</entry><entry>uint8_t</entry><entry>update_period</entry>
+             <entry>How often telemetry is sent, in 1/100ths of a second</entry>
+           </row>
+           <row>
+             <entry>7</entry><entry>uint8_t</entry><entry>channels</entry>
+             <entry>Number of data channels supplied</entry>
+           </row>
+           <row>
+             <entry>8</entry><entry>uint16_t[12]</entry><entry>companion_data</entry>
+             <entry>Up to 12 channels of 16-bit companion data</entry>
+           </row>
+           <row>
+             <entry>32</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.
+      Altus Metrum devices use Texas Instruments sub-GHz digital radio
+      products. Ground stations use parts with HW FEC while some
+      flight computers perform FEC in software. TeleGPS is
+      transmit-only.
     </para>
+    <table>
+      <title>Altus Metrum Radio Parts</title>
+      <tgroup cols='3'>
+       <colspec align="center" colwidth="*" colname="Part Number"/>
+       <colspec align="center" colwidth="*" colname="Description"/>
+       <colspec align="left" colwidth="*" colname="Used in"/>
+       <thead>
+         <row>
+           <entry align="center">Part Number</entry>
+           <entry align="center">Description</entry>
+           <entry align="center">Used in</entry>
+         </row>
+       </thead>
+       <tbody>
+         <row>
+           <entry>CC1111</entry><entry>10mW transceiver with integrated SoC</entry>
+           <entry>TeleDongle v0.2, TeleBT v1.0, TeleMetrum v1.x, TeleMini</entry>
+         </row>
+         <row>
+           <entry>CC1120</entry><entry>35mW transceiver with SW FEC</entry>
+           <entry>TeleMetrum v2, TeleMega</entry>
+         </row>
+         <row>
+           <entry>CC1200</entry><entry>35mW transceiver with HW FEC</entry>
+           <entry>TeleDongle v3.0, TeleBT v3.0</entry>
+         </row>
+         <row>
+           <entry>CC115L</entry><entry>14mW transmitter with SW FEC</entry>
+           <entry>TeleGPS</entry>
+         </row>
+       </tbody>
+      </tgroup>
+    </table>
     <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:
+       rate.
+
+       While we might like to use something with better low-signal
+       performance like BPSK, the radios we use don't support that,
+       but do support Gaussian frequency shift keying (GFSK). Regular
+       frequency shift keying (FSK) encodes the signal by switching
+       the carrier between two frequencies. The Gaussian version is
+       essentially the same, but the shift between frequencies gently
+       follows a gaussian curve, rather than switching
+       immediately. This tames the bandwidth of the signal without
+       affecting the ability to transmit data.
+
+       For AltOS, there are three available bit rates, 38.4kBaud,
+       9.6kBaud and 2.4kBaud resulting in the following signal
+       parmeters:
+
       </para>
       <table>
        <title>Modulation Scheme</title>
        <tgroup cols='3'>
-         <colspec align="center" colwidth="*" colname="parameter"/>
-         <colspec align="center" colwidth="*" colname="value"/>
-         <colspec align="center" colwidth="*" colname="description"/>
+         <colspec align="center" colwidth="*" colname="rate"/>
+         <colspec align="center" colwidth="*" colname="deviation"/>
+         <colspec align="center" colwidth="*" colname="bandwidth"/>
          <thead>
            <row>
-             <entry align='center'>Parameter</entry>
-             <entry align='center'>Value</entry>
-             <entry align='center'>Description</entry>
+             <entry align='center'>Rate</entry>
+             <entry align='center'>Deviation</entry>
+             <entry align='center'>Receiver Bandwidth</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>
+             <entry>38.4kBaud</entry>
+             <entry>20.5kHz</entry>
+             <entry>100kHz</entry>
            </row>
            <row>
-             <entry>RX Filter Bandwidth</entry>
-             <entry>93.75 kHz</entry>
-             <entry>Receiver Band pass filter bandwidth</entry>
+             <entry>9.6kBaud</entry>
+             <entry>5.125kHz</entry>
+             <entry>25kHz</entry>
            </row>
            <row>
-             <entry>IF Frequency</entry>
-             <entry>140.62 kHz</entry>
-             <entry>Receiver intermediate frequency</entry>
+             <entry>2.4kBaud</entry>
+             <entry>1.5kHz</entry>
+             <entry>5kHz</entry>
            </row>
          </tbody>
        </tgroup>
     <section>
       <title>Error Correction</title>
       <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.
+       The cc1111 and cc1200 provide forward error correction in
+       hardware; on the cc1120 and cc115l that's done in
+       software. AltOS uses this to improve reception of weak
+       signals. As it's a rate 1/2 encoding, each bit of data takes
+       two bits when transmitted, so the effective data rate is half
+       of the raw transmitted bit rate.
       </para>
       <table>
        <title>Error Correction</title>
index c08e9236e91ac75392edd300dea1f9440d1af2e4..af238ac42527da025ff403481c046ba58146b0b7 100644 (file)
@@ -150,14 +150,21 @@ SUFFIXES=.svg .build .icns .ico .rc .o .exe
        icotool -c -o $@ $(shell for i in $(WIN_RES); do echo $*-$$i.png; done)
 
 .ico.rc:
-       echo '101 ICON "$*.ico"' > $@
+       ./make-rc "$*" $(VERSION) > $@
 
 
 MINGCC32=i686-w64-mingw32-gcc
 MINGWINDRES=i686-w64-mingw32-windres
+MINGFLAGS=-Wall -DWINDOWS -mwindows
+MINGLIBS=-lshlwapi
 
 .rc.o:
        $(MINGWINDRES) $*.rc $@
 
 .o.exe:
-       $(MINGCC32) -o $@ windows-stub.c $*.o
+       $(MINGCC32) -o $@ $(MINGFLAGS) windows-stub.o $*.o $(MINGLIBS)
+
+$(EXE_FILES): windows-stub.o make-rc
+
+windows-stub.o: windows-stub.c
+       $(MINGCC32) -c $(MINGFLAGS) windows-stub.c
diff --git a/icon/make-rc b/icon/make-rc
new file mode 100755 (executable)
index 0000000..de64727
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+COMPANY="Altus Metrum, LLC"
+PRODUCT="Altus Metrum"
+
+case "$1" in
+    *altosui*)
+       PRODUCT="AltosUI"
+       ;;
+    *telegps*)
+       PRODUCT="TeleGPS"
+       ;;
+    *micropeak*)
+       PRODUCT="MicroPeak"
+       ;;
+esac
+
+VERSION="$2"
+VERSION_COMMA=`echo "$VERSION" | sed 's/\./,/g'`
+INTERNAL_NAME=`basename $1`
+EXE_NAME="$INTERNAL_NAME".exe
+YEAR=`date +%Y`
+
+cat <<EOF
+101 ICON "$1.ico"
+1 VERSIONINFO
+FILEVERSION $VERSION_COMMA
+PRODUCTVERSION $VERSION_COMMA
+FILEFLAGSMASK 0
+FILEOS 0x40004
+FILETYPE 1
+{
+ BLOCK "StringFileInfo"
+ {
+  BLOCK "040904E4"
+  {
+   VALUE "Comments", "$COMPANY $PRODUCT"
+   VALUE "CompanyName", "$COMPANY"
+   VALUE "FileDescription", "$PRODUCT"
+   VALUE "FileVersion", "$VERSION"
+   VALUE "InternalName", "$INTERNAL_NAME"
+   VALUE "LegalCopyright", "Copyright $YEAR, $COMPANY"
+   VALUE "OriginalFilename", "$EXE_NAME"
+   VALUE "ProductName", "$PRODUCT"
+   VALUE "ProductVersion", "$VERSION"
+  }
+ }
+ BLOCK "VarFileInfo"
+ {
+  VALUE "Translation", 0x409, 1252
+ }
+}
+EOF
index 8df3e0aafc215dc2b81dae1a8191843960c936f6..e075a02d9dff4f91b41f2b0a2aac00da7c685c20 100644 (file)
@@ -1,2 +1,201 @@
-__stdcall
-WinMain(int a, int b, int c, int d) { return 0; }
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+/* A windows stub program to launch a java program with suitable parameters
+ *
+ * Given that the name of this exe is altusmetrum-foo.exe living in directory bar, and
+ * that it was run with 'args' extra command line parameters, run:
+ *
+ *     javaw.exe -Djava.library.path="bar" -jar "bar/foo-fat.jar" args
+ */
+
+#define _UNICODE
+#define UNICODE
+#include <stdlib.h>
+#include <windows.h>
+#include <setupapi.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <shlwapi.h>
+
+/* Concatenate a list of strings together
+ */
+static LPTSTR
+wcsbuild(LPTSTR first, ...)
+{
+       va_list args;
+       int     len;
+       LPTSTR  buf;
+       LPTSTR  arg;
+
+       buf = wcsdup(first);
+       va_start(args, first);
+       while ((arg = va_arg(args, LPTSTR)) != NULL) {
+               len = wcslen(buf) + wcslen(arg) + 1;
+               buf = realloc(buf, len * sizeof (wchar_t));
+               wcscat(buf, arg);
+       }
+       va_end(args);
+       return buf;
+}
+
+/* Quote a single string, taking care to escape embedded quote and
+ * backslashes within
+ */
+static LPTSTR
+quote_arg(LPTSTR arg)
+{
+       LPTSTR  result;
+       LPTSTR  in, out;
+       int     out_len = 3;    /* quotes and terminating null */
+
+       /* Find quote and backslashes */
+       for (in = arg; *in; in++) {
+               switch (*in) {
+               case '"':
+               case '\\':
+                       out_len += 2;
+                       break;
+               default:
+                       out_len++;
+                       break;
+               }
+       }
+
+       result = malloc ((out_len + 1) * sizeof (wchar_t));
+       out = result;
+       *out++ = '"';
+       for (in = arg; *in; in++) {
+               switch (*in) {
+               case '"':
+               case '\\':
+                       *out++ = '\\';
+                       break;
+               }
+               *out++ = *in;
+       }
+       *out++ = '"';
+       *out++ = '\0';
+       return result;
+}
+
+/* Construct a single string from a list of arguments
+ */
+static LPTSTR
+quote_args(LPTSTR *argv, int argc)
+{
+       LPTSTR  result = NULL, arg;
+       int     i;
+
+       result = malloc(1 * sizeof (wchar_t));
+       result[0] = '\0';
+       for (i = 0; i < argc; i++) {
+               arg = quote_arg(argv[i]);
+               result = realloc(result, (wcslen(result) + 1 + wcslen(arg) + 1) * sizeof (wchar_t));
+               wcscat(result, L" ");
+               wcscat(result, arg);
+               free(arg);
+       }
+       return result;
+}
+
+/* Return the directory portion of the provided file
+ */
+static LPTSTR
+get_dir(LPTSTR file)
+{
+       DWORD   len = GetFullPathName(file, 0, NULL, NULL);
+       LPTSTR  full = malloc (len * sizeof (wchar_t));
+       GetFullPathName(file, len, full, NULL);
+       PathRemoveFileSpec(full);
+       return full;
+}
+
+/* Convert a .exe name into a -fat.jar name, starting
+ * by computing the complete path name of the source filename
+ */
+static LPTSTR
+make_jar(LPTSTR file)
+{
+       DWORD   len = GetFullPathName(file, 0, NULL, NULL);
+       LPTSTR  full = malloc (len * sizeof (wchar_t));
+       LPTSTR  base_part;
+       LPTSTR  jar;
+       LPTSTR  dot;
+       GetFullPathName(file, len, full, &base_part);
+       static const wchar_t head[] = L"altusmetrum-";
+
+       if (wcsncmp(base_part, head, wcslen(head)) == 0)
+               base_part += wcslen(head);
+       dot = wcsrchr(base_part, '.');
+       if (dot)
+               *dot = '\0';
+       jar = wcsdup(base_part);
+       PathRemoveFileSpec(full);
+       return wcsbuild(full, L"\\", jar, L"-fat.jar", NULL);
+}
+
+/* Build the complete command line from the pieces
+ */
+static LPTSTR
+make_cmd(LPTSTR dir, LPTSTR jar, LPTSTR quote_args)
+{
+       LPTSTR  quote_dir = quote_arg(dir);
+       LPTSTR  quote_jar = quote_arg(jar);
+       LPTSTR  cmd;
+
+       cmd = wcsbuild(L"javaw.exe -Djava.library.path=", quote_dir, L" -jar ", quote_jar, quote_args, NULL);
+       free(quote_jar);
+       free(jar);
+       free(quote_dir);
+       return cmd;
+}
+
+int WINAPI
+WinMain(HINSTANCE instance, HINSTANCE prev_instance, LPSTR cmd_line_a, int cmd_show)
+{
+       STARTUPINFO             startup_info;
+       PROCESS_INFORMATION     process_information;
+       BOOL                    result;
+       wchar_t                 *command_line;
+       int                     argc;
+       LPTSTR                  *argv = CommandLineToArgvW(GetCommandLine(), &argc);
+       LPTSTR                  my_dir;
+       LPTSTR                  my_jar;
+       LPTSTR                  args = quote_args(argv + 1, argc - 1);
+
+       my_dir = get_dir(argv[0]);
+       my_jar = make_jar(argv[0]);
+       command_line = make_cmd(my_dir, my_jar, args);
+       memset(&startup_info, '\0', sizeof startup_info);
+       startup_info.cb = sizeof startup_info;
+       result = CreateProcess(NULL,
+                              command_line,
+                              NULL,
+                              NULL,
+                              FALSE,
+                              CREATE_NO_WINDOW,
+                              NULL,
+                              NULL,
+                              &startup_info,
+                              &process_information);
+       if (result) {
+               CloseHandle(process_information.hProcess);
+               CloseHandle(process_information.hThread);
+       }
+       exit(0);
+}
index 158656068c963d2f7675cb0621eeee9806a96785..a8834a66e6c0bd5b16384a888d6fa0e52b968939 100644 (file)
@@ -124,20 +124,29 @@ FAT_FILES=$(FATJAR) $(ALTOSLIB_CLASS) $(ALTOSUILIB_CLASS) $(FREETTS_CLASS) $(JFR
 LINUX_FILES=$(FAT_FILES) libaltos.so $(FIRMWARE) $(DOC) $(desktop_file).in $(LINUX_ICONS) $(LINUX_MIMETYPE)
 LINUX_EXTRA=micropeak-fat $(desktop_file).in
 
-MACOSX_DRIVER_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_2_18.dmg
-MACOSX_DRIVER=FTDIUSBSerialDriver_v2_2_18.dmg
+MACOSX_DRIVER_0_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_2_18.dmg
+MACOSX_DRIVER_0=FTDI_v2_2_18.dmg
+
+MACOSX_DRIVER_1_URL=http://www.ftdichip.com/Drivers/VCP/MacOSX/FTDIUSBSerialDriver_v2_3.dmg
+MACOSX_DRIVER_1=FTDI_v2_3.dmg
+
+MACOSX_DRIVERS=$(MACOSX_DRIVER_1) $(MACOSX_DRIVER_0)
+
 MACOSX_INFO_PLIST=Info.plist
 MACOSX_README=ReadMe-Mac.rtf
-MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVER) $(MACOSX_README) $(DOC) $(MACOSX_ICONS)
+MACOSX_FILES=$(FAT_FILES) libaltos.dylib $(MACOSX_INFO_PLIST) $(MACOSX_DRIVERS) $(MACOSX_README) $(DOC) $(MACOSX_ICONS)
+
+$(MACOSX_DRIVER_0):
+       wget -O $@ $(MACOSX_DRIVER_0_URL)
 
-$(MACOSX_DRIVER):
-       wget $(MACOSX_DRIVER_URL)
+$(MACOSX_DRIVER_1):
+       wget -O $@ $(MACOSX_DRIVER_1_URL)
 
-WINDOWS_DRIVER_URL=http://www.ftdichip.com/Drivers/CDM/CDM20824_Setup.exe
-WINDOWS_DRIVER=CDM20824_Setup.exe
+WINDOWS_DRIVER_URL=http://www.ftdichip.com/Drivers/CDM/CDM%20v2.12.00%20WHQL%20Certified.exe
+WINDOWS_DRIVER=CDM_v2.12.00_WHQL_Certified.exe
 
 $(WINDOWS_DRIVER):
-       wget $(WINDOWS_DRIVER_URL)
+       wget -O "$(WINDOWS_DRIVER)" "$(WINDOWS_DRIVER_URL)"
 
 WINDOWS_FILES=$(FAT_FILES) altos.dll altos64.dll $(DOC) $(WINDOWS_ICONS) $(WINDOWS_DRIVER)
 
@@ -274,7 +283,7 @@ $(MACOSX_DIST): $(MACOSX_FILES)
        cp -a $(MACOSX_README) macosx/ReadMe.rtf
        cp -a $(DOC) macosx
        cp -p Info.plist macosx/MicroPeak.app/Contents
-       cp -p $(MACOSX_DRIVER) macosx
+       cp -p $(MACOSX_DRIVERS) macosx
        mkdir -p macosx/MicroPeak.app/Contents/Resources/Java
        cp -p $(MACOSX_ICONS) macosx/MicroPeak.app/Contents/Resources
        cp -p $(FATJAR) macosx/MicroPeak.app/Contents/Resources/Java/micropeak.jar
index 857dbe584b0c157a14f7bd322f288eb7e1848bb9..62f03c9d140b8dd98234f12e7e165631a366aba5 100644 (file)
@@ -20,8 +20,8 @@ package org.altusmetrum.micropeak;
 import java.lang.*;
 import java.io.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 class MicroIterator implements Iterator<MicroDataPoint> {
        int             i;
index 45d5ba273fba13bfa45850ecbdfe41eb5c117b20..1c36fefeac6d057995fabe80bf212fe2aaaa1b08 100644 (file)
@@ -17,7 +17,7 @@
 
 package org.altusmetrum.micropeak;
 
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroDataPoint implements AltosUIDataPoint {
        public double           time;
index 5532848261fc6802de7d7e927c35dc78dbe247f9..a6d0f3213970f4f5d70cddcf0370d42a38ad8a3e 100644 (file)
@@ -21,7 +21,7 @@ import javax.swing.*;
 import java.awt.*;
 import java.awt.event.*;
 import java.util.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroDeviceDialog extends AltosDeviceDialog {
 
index a03d7b3ac4a9d98b494e91a149681e4dbae538fd..7314cc7f91d2f7c38dd4a248fb47582d667dbc01 100644 (file)
@@ -23,8 +23,8 @@ import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroDownload extends AltosUIDialog implements Runnable, ActionListener, MicroSerialLog, WindowListener {
        MicroPeak       owner;
index 93c8cae8d34284be02835c0ae84fad995a1705e5..d3cfc3b8c9979306341841a5cc8d4fb3899bd5b8 100644 (file)
@@ -23,8 +23,8 @@ import java.util.ArrayList;
 import java.awt.*;
 import javax.swing.*;
 import javax.swing.filechooser.FileNameExtensionFilter;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroExport extends JFileChooser {
 
index 78f7dac2175fba3f42b71cd90209cf56f8e477e1..d48598cb01055fe910a08ef2f0c179b8a891a759 100644 (file)
@@ -19,8 +19,8 @@ package org.altusmetrum.micropeak;
 
 import java.io.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroFile {
 
index 5ac22e3049306900aa16fb691e6df43349df0ed0..d72d956ab7311bb7bf55bf0a1ed5e4d68b611841 100644 (file)
@@ -20,8 +20,8 @@ package org.altusmetrum.micropeak;
 import javax.swing.*;
 import javax.swing.filechooser.FileNameExtensionFilter;
 import java.io.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroFileChooser extends JFileChooser {
        JFrame  frame;
index 47b03a12cb97799bfb4b15a033828a9c6e820cc2..9b0f1e7c5e0c2ec23a2fc7921a758f7b0bf35a59 100644 (file)
@@ -21,7 +21,7 @@ import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import java.util.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroFrame extends AltosUIFrame {
        static String[] micro_icon_names = {
index a6d511c3c8d428fa8bde5fd4d3b7edacbfe39c1a..c7a2d345a97b02c2ef19c5962858249c390fea7e 100644 (file)
@@ -22,8 +22,8 @@ import java.util.ArrayList;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 import org.jfree.ui.*;
 import org.jfree.chart.*;
index 9f7095f36248ebd85105cd00dd389c65d9041ad0..2df81621053c36c2186634501f254b6ff64478dd 100644 (file)
@@ -23,8 +23,8 @@ import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroPeak extends MicroFrame implements ActionListener, ItemListener {
 
index 587fe927cf3a5849f184145fa4520a5aa790f590..6aae0bde176d62a15eaddbdee90fac90581d0130 100644 (file)
@@ -20,8 +20,8 @@ package org.altusmetrum.micropeak;
 import java.awt.*;
 import java.io.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroRaw extends JTextArea {
 
index 65026bdaacf09084c21f72d9de7aa47f8e4149d9..d8fe59c2c6249d4d66e818fb299d3eb1340111f4 100644 (file)
@@ -24,8 +24,8 @@ import javax.swing.filechooser.FileNameExtensionFilter;
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroSave extends JFileChooser {
 
index 4282aba170c67db33a8117a1badbb1c9d5dd9098..8d20718f7f0c8f2ef950ee0f034db2833da614c9 100644 (file)
@@ -20,7 +20,7 @@ package org.altusmetrum.micropeak;
 import java.util.*;
 import java.io.*;
 import libaltosJNI.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroSerial extends InputStream {
        SWIGTYPE_p_altos_file   file;
index 3ee44649e8a4a54e8ab4fec9ae5a82e24c91edf6..c7a0525d8347e11214e623cc2b41002b401c1033 100644 (file)
@@ -20,7 +20,7 @@ package org.altusmetrum.micropeak;
 import java.util.*;
 import java.io.*;
 import libaltosJNI.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public interface MicroSerialLog {
 
index 47ef2a799547d3fbc4d10d8cfa4adbb6024d6027..8d83fe3e97bc38d69960f79249cc2fa2bb4cb8d5 100644 (file)
@@ -18,8 +18,8 @@
 package org.altusmetrum.micropeak;
 
 import java.io.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroStats {
        double          coast_height;
index 139c4416280ce958011baead0a1370d1732c21a3..35d10eeeb329019265678dae82dc096b7c0efcbd 100644 (file)
@@ -19,8 +19,8 @@ package org.altusmetrum.micropeak;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroStatsTable extends JComponent implements AltosFontListener {
        GridBagLayout   layout;
index fa20488d9c77d4ed357803d1f408a4d91b48029d..3a7891cbc95832166251435c59d3d97b466f56e9 100644 (file)
@@ -19,8 +19,8 @@ package org.altusmetrum.micropeak;
 
 import java.util.*;
 import libaltosJNI.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class MicroUSB extends altos_device implements AltosDevice {
 
index a3779630630b40ded1c6b3b8756b6e491f957f23..c3b63f7b31d6b3215764a4a601973178a8d02225 100644 (file)
@@ -65,9 +65,9 @@ UninstPage instfiles
 Section "FTDI USB Driver"
        SetOutPath $INSTDIR
 
-       File "CDM20824_Setup.exe"
+       File "CDM_v2.12.00_WHQL_Certified.exe"
 
-       StrCpy $2 "$INSTDIR\CDM20824_Setup.exe"
+       StrCpy $2 "$INSTDIR\CDM_v2.12.00_WHQL_Certified.exe"
        ExecWait $2
 SectionEnd
 
@@ -104,16 +104,17 @@ Section "${REG_NAME} Application"
        File "altosuilib_@ALTOSUILIB_VERSION@.jar"
        File "jfreechart.jar"
        File "jcommon.jar"
+       File "../icon/${WIN_APP_EXE}"
 
        File "*.dll"
 
        File "../icon/${WIN_APP_ICON}"
 
-       CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+       CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
 SectionEnd
 
 Section "${REG_NAME} Desktop Shortcut"
-       CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}"  "" "$INSTDIR\${WIN_APP_ICON}"
+       CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}"  "" "$INSTDIR\${WIN_APP_ICON}"
 SectionEnd
 
 Section "Documentation"
@@ -129,11 +130,8 @@ Section "File Associations"
 
        SetOutPath $INSTDIR
 
-       File "../icon/${WIN_APP_EXE}"
        File "../icon/${WIN_MPD_EXE}"
 
-       SearchPath $1 "javaw.exe"
-
        ; application elements
        
        DeleteRegKey HKCR "${PROG_ID}"
@@ -143,7 +141,7 @@ Section "File Associations"
        WriteRegStr HKCR "${PROG_ID_MPD}"               "FriendlyTypeName"              "MicroPeak Data File"
        WriteRegStr HKCR "${PROG_ID_MPD}\CurVer"        ""                              "${PROG_ID_MPD}"
        WriteRegStr HKCR "${PROG_ID_MPD}\DefaultIcon"   ""                              '"$INSTDIR\${WIN_MPD_EXE}",-101'
-  WriteRegExpandStr HKCR "${PROG_ID_MPD}\shell\play\command" ""                                '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+  WriteRegExpandStr HKCR "${PROG_ID_MPD}\shell\play\command" ""                                '"$INSTDIR\${WIN_APP_EXE}" "%1"'
 
        ; .mpd elements
 
index 05e99c7fbfd77405ecabb89694cff4aa2662de00..dc74bf8ca8925290b82414eba3aecf7f667cf5bf 100644 (file)
@@ -34,13 +34,15 @@ ARMM3DIRS=\
        telegps-v0.3 telegps-v0.3/flash-loader \
        telegps-v1.0 telegps-v1.0/flash-loader \
        telelco-v0.2 telelco-v0.2/flash-loader \
+       telelco-v0.3 telelco-v0.3/flash-loader \
        telescience-v0.2 telescience-v0.2/flash-loader \
        teledongle-v3.0 teledongle-v3.0/flash-loader \
        teleballoon-v2.0 \
        telebt-v3.0 telebt-v3.0/flash-loader
 
 ARMM0DIRS=\
-       easymini-v1.0 easymini-v1.0/flash-loader
+       easymini-v1.0 easymini-v1.0/flash-loader \
+       chaoskey-v0.1 chaoskey-v0.1/flash-loader
 
 AVRDIRS=\
        telescience-v0.1 telescience-pwm micropeak nanopeak-v0.1 microkite
index b3c6b5dcce6fd29ffa7710ec47c1ecb5293a950d..6eb1a11101c6f473d09b950b60ae0d8ee386ff28 100644 (file)
@@ -228,7 +228,7 @@ void
 ao_button_init(void);
 
 char
-ao_button_get(void) __critical;
+ao_button_get(uint16_t timeout) __critical;
 
 void
 ao_button_clear(void) __critical;
index 69f3475fe660d43125073a40b19118f3f545c07d..a0f221c2780ce0c6df18935f2996aaab8200159c 100644 (file)
@@ -72,12 +72,12 @@ ao_button_mask(uint8_t reg)
 }
 
 char
-ao_button_get(void) __critical
+ao_button_get(uint16_t timeout) __critical
 {
        char    b;
 
        while (ao_fifo_empty(ao_button_fifo))
-               if (ao_sleep(&ao_button_fifo))
+               if (ao_sleep_for(&ao_button_fifo, timeout))
                        return 0;
        ao_fifo_remove(ao_button_fifo, b);
        return b;
index b9821a4237ab950caecebb6bac85defb30ccbff2..cead036419f18c97a9ae7277564a849242d5866e 100644 (file)
@@ -451,13 +451,9 @@ ao_radio_recv(__xdata void *packet, uint8_t size, uint8_t timeout) __reentrant
        /* Wait for DMA to be done, for the radio receive process to
         * get aborted or for a receive timeout to fire
         */
-       if (timeout)
-               ao_alarm(timeout);
        __critical while (!ao_radio_dma_done && !ao_radio_abort)
-                          if (ao_sleep(&ao_radio_dma_done))
+                          if (ao_sleep_for(&ao_radio_dma_done, timeout))
                                   break;
-       if (timeout)
-               ao_clear_alarm();
 
        /* If recv was aborted, clean up by stopping the DMA engine
         * and idling the radio
diff --git a/src/chaoskey-v0.1/.gitignore b/src/chaoskey-v0.1/.gitignore
new file mode 100644 (file)
index 0000000..b0adba2
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+chaoskey-*
diff --git a/src/chaoskey-v0.1/Makefile b/src/chaoskey-v0.1/Makefile
new file mode 100644 (file)
index 0000000..ac4a678
--- /dev/null
@@ -0,0 +1,70 @@
+#
+# AltOS build
+#
+#
+
+include ../stmf0/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_pins.h \
+       ao_product.h \
+       ao_task.h \
+       ao_adc_fast.h \
+       stm32f0.h
+
+#
+# Common AltOS sources
+#
+ALTOS_SRC = \
+       ao_interrupt.c \
+       ao_timer.c \
+       ao_panic.c \
+       ao_mutex.c \
+       ao_dma_stm.c \
+       ao_adc_fast.c \
+       ao_crc_stm.c \
+       ao_stdio.c \
+       ao_led.c \
+       ao_romconfig.c \
+       ao_boot_chain.c \
+       ao_usb_stm.c \
+       ao_trng_send.c \
+       ao_task.c \
+       ao_product.c
+
+PRODUCT=ChaosKey-v0.1
+PRODUCT_DEF=-DCHAOSKEY_V_0_1
+IDVENDOR=0x1d50
+IDPRODUCT=0x60c6
+
+CFLAGS = $(PRODUCT_DEF) $(STMF0_CFLAGS) -g -Os
+
+PROGNAME=chaoskey-v0.1
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_chaoskey.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+ao_product.h: ao-make-product.5c ../Version
+       $(call quiet,NICKLE,$<) $< -m altusmetrum.org -V $(IDVENDOR) -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) -o $@
+
+$(OBJ): $(INC)
+
+distclean:     clean
+
+clean:
+       rm -f *.o $(PROGNAME)-*.elf $(PROGNAME)-*.ihx
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/chaoskey-v0.1/ao_chaoskey.c b/src/chaoskey-v0.1/ao_chaoskey.c
new file mode 100644 (file)
index 0000000..48c8bf0
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2014 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 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_adc_fast.h>
+#include <ao_crc.h>
+#include <ao_trng_send.h>
+
+void main(void)
+{
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_RED);
+       ao_clock_init();
+       ao_task_init();
+       ao_timer_init();
+       ao_dma_init();
+       ao_adc_init();
+       ao_crc_init();
+
+       ao_usb_init();
+
+       ao_trng_send_init();
+
+       ao_led_off(AO_LED_RED);
+
+       ao_start_scheduler();
+}
diff --git a/src/chaoskey-v0.1/ao_pins.h b/src/chaoskey-v0.1/ao_pins.h
new file mode 100644 (file)
index 0000000..72963db
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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 LED_PORT_ENABLE        STM_RCC_AHBENR_IOPAEN
+#define LED_PORT       (&stm_gpioa)
+#define LED_PIN_RED    2
+#define LED_PIN_GREEN  3
+#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_BEEP       0
+
+/* 48MHz clock based on USB */
+#define AO_HSI48       1
+
+/* HCLK = 48MHz */
+#define AO_AHB_PRESCALER       1
+#define AO_RCC_CFGR_HPRE_DIV   STM_RCC_CFGR_HPRE_DIV_1
+
+/* APB = 48MHz */
+#define AO_APB_PRESCALER       1
+#define AO_RCC_CFGR_PPRE_DIV   STM_RCC_CFGR_PPRE_DIV_1
+
+#define HAS_USB                        1
+#define AO_USB_DIRECTIO                1
+#define AO_PA11_PA12_RMP       0
+#define AO_USB_INTERFACE_CLASS 0xff
+
+#define IS_FLASH_LOADER        0
+
+/* ADC */
+
+#define AO_ADC_PIN0_PORT       (&stm_gpioa)
+#define AO_ADC_PIN0_PIN                6
+#define AO_ADC_PIN0_CH         6
+
+#define AO_ADC_RCC_AHBENR      ((1 << STM_RCC_AHBENR_IOPAEN))
+
+#define AO_NUM_ADC             1
+
+/* CRC */
+#define AO_CRC_WIDTH   32
+#define AO_CRC_INIT    0xffffffff
+
+/* TRNG */
+#define AO_LED_TRNG_ACTIVE     AO_LED_GREEN
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/chaoskey-v0.1/flash-loader/.gitignore b/src/chaoskey-v0.1/flash-loader/.gitignore
new file mode 100644 (file)
index 0000000..a60a494
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+chaoskey*
diff --git a/src/chaoskey-v0.1/flash-loader/Makefile b/src/chaoskey-v0.1/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..4f61a24
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=chaoskey-v0.1
+include $(TOPDIR)/stmf0/Makefile-flash.defs
diff --git a/src/chaoskey-v0.1/flash-loader/ao_pins.h b/src/chaoskey-v0.1/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..295e025
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_
+
+#include <ao_flash_stm_pins.h>
+
+/* Pin 5 on debug connector */
+
+#define AO_BOOT_PIN                    1
+#define AO_BOOT_APPLICATION_GPIO       stm_gpioa
+#define AO_BOOT_APPLICATION_PIN                15
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_EXTI_MODE_PULL_UP
+
+/* USB */
+#define HAS_USB                        1
+#define AO_USB_DIRECTIO                0
+#define AO_PA11_PA12_RMP       0
+
+#endif /* _AO_PINS_H_ */
index 19beb78f2b51dbf89f1f0f44563d50b203357bc2..a8016673a0fde1b4450a3ed2431fb91c05d0ca60 100644 (file)
@@ -707,8 +707,7 @@ static int tncPositionPacket(void)
     static int32_t     latitude;
     static int32_t     longitude;
     static int32_t     altitude;
-    int32_t            lat, lon, alt;
-    uint8_t    *buf;
+    uint8_t            *buf;
 
     if (ao_gps_data.flags & AO_GPS_VALID) {
        latitude = ao_gps_data.latitude;
@@ -719,28 +718,99 @@ static int tncPositionPacket(void)
     }
 
     buf = tncBuffer;
-    *buf++ = '!';
 
-    /* Symbol table ID */
-    *buf++ = '/';
+#ifdef AO_APRS_TEST
+#define AO_APRS_FORMAT_COMPRESSED      0
+#define AO_APRS_FORMAT_UNCOMPRESSED    1
+    switch (AO_APRS_FORMAT_COMPRESSED) {
+#else
+    switch (ao_config.aprs_format) {
+#endif
+    case AO_APRS_FORMAT_COMPRESSED:
+    default:
+    {
+           int32_t             lat, lon, alt;
+
+           *buf++ = '!';
+
+           /* Symbol table ID */
+           *buf++ = '/';
+
+           lat = ((uint64_t) 380926 * (900000000 - latitude)) / 10000000;
+           lon = ((uint64_t) 190463 * (1800000000 + longitude)) / 10000000;
+
+           alt = ao_aprs_encode_altitude(altitude);
 
-    lat = ((uint64_t) 380926 * (900000000 - latitude)) / 10000000;
-    lon = ((uint64_t) 190463 * (1800000000 + longitude)) / 10000000;
+           tncCompressInt(buf, lat, 4);
+           buf += 4;
+           tncCompressInt(buf, lon, 4);
+           buf += 4;
 
-    alt = ao_aprs_encode_altitude(altitude);
+           /* Symbol code */
+           *buf++ = '\'';
 
-    tncCompressInt(buf, lat, 4);
-    buf += 4;
-    tncCompressInt(buf, lon, 4);
-    buf += 4;
+           tncCompressInt(buf, alt, 2);
+           buf += 2;
 
-    /* Symbol code */
-    *buf++ = '\'';
+           *buf++ = 33 + ((1 << 5) | (2 << 3));
 
-    tncCompressInt(buf, alt, 2);
-    buf += 2;
+           break;
+    }
+    case AO_APRS_FORMAT_UNCOMPRESSED:
+    {
+           char        lat_sign = 'N', lon_sign = 'E';
+           int32_t     lat = latitude;
+           int32_t     lon = longitude;
+           int32_t     alt = altitude;
+           uint16_t    lat_deg;
+           uint16_t    lon_deg;
+           uint16_t    lat_min;
+           uint16_t    lat_frac;
+           uint16_t    lon_min;
+           uint16_t    lon_frac;
+
+           if (lat < 0) {
+                   lat_sign = 'S';
+                   lat = -lat;
+           }
 
-    *buf++ = 33 + ((1 << 5) | (2 << 3));
+           if (lon < 0) {
+                   lon_sign = 'W';
+                   lon = -lon;
+           }
+
+           /* Round latitude and longitude by 0.005 minutes */
+           lat = lat + 833;
+           if (lat > 900000000)
+                   lat = 900000000;
+           lon = lon + 833;
+           if (lon > 1800000000)
+                   lon = 1800000000;
+
+           lat_deg = lat / 10000000;
+           lat -= lat_deg * 10000000;
+           lat *= 60;
+           lat_min = lat / 10000000;
+           lat -= lat_min * 10000000;
+           lat_frac = lat / 100000;
+
+           lon_deg = lon / 10000000;
+           lon -= lon_deg * 10000000;
+           lon *= 60;
+           lon_min = lon / 10000000;
+           lon -= lon_min * 10000000;
+           lon_frac = lon / 100000;
+
+           /* Convert from meters to feet */
+           alt = (alt * 328 + 50) / 100;
+
+           buf += sprintf((char *) tncBuffer, "!%02u%02u.%02u%c/%03u%02u.%02u%c'/A=%06u ",
+                          lat_deg, lat_min, lat_frac, lat_sign,
+                          lon_deg, lon_min, lon_frac, lon_sign,
+                          alt);
+           break;
+    }
+    }
 
     buf += tncComment(buf);
 
index e6b286881e778a13e54049c849f91f4f0f714e61..8e7052cb413000059c259587282121b0c60d4474 100644 (file)
@@ -23,7 +23,7 @@
 #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_sleep() ao_sleep((void *) &ao_serial1_rx_fifo)
+#define _ao_serial_btm_sleep_for(timeout)      ao_sleep_for((void *) &ao_serial1_rx_fifo, timeout)
 #define ao_serial_btm_set_speed ao_serial1_set_speed
 #define ao_serial_btm_drain    ao_serial1_drain
 #endif
@@ -111,7 +111,7 @@ ao_btm_do_echo(void)
        while (ao_btm_enable) {
                ao_arch_block_interrupts();
                while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN && ao_btm_enable)
-                       _ao_serial_btm_sleep();
+                       _ao_serial_btm_sleep_for(0);
                ao_arch_release_interrupts();
                if (c != AO_READ_AGAIN) {
                        putchar(c);
@@ -166,9 +166,7 @@ ao_btm_getchar(void)
 
        ao_arch_block_interrupts();
        while ((c = _ao_serial_btm_pollchar()) == AO_READ_AGAIN) {
-               ao_alarm(AO_MS_TO_TICKS(10));
-               c = _ao_serial_btm_sleep();
-               ao_clear_alarm();
+               c = _ao_serial_btm_sleep_for(AO_MS_TO_TICKS(10));
                if (c) {
                        c = AO_READ_AGAIN;
                        break;
@@ -265,6 +263,15 @@ uint8_t
 ao_btm_cmd(__code char *cmd)
 {
        ao_btm_drain();
+
+#ifdef AO_BTM_INT_PORT
+       /* Trust that AltosDroid will eventually disconnect and let us
+        * get things set up. The BTM module doesn't appear to listen
+        * for +++, so we have no way to force a disconnect.
+        */
+       while (ao_btm_connected)
+               ao_sleep(&ao_btm_connected);
+#endif
        ao_btm_string(cmd);
        return ao_btm_wait_reply();
 }
@@ -352,6 +359,10 @@ __xdata struct ao_task ao_btm_task;
 void
 ao_btm(void)
 {
+#ifdef AO_BTM_INT_PORT
+       ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN);
+#endif
+
        /*
         * Wait for the bluetooth device to boot
         */
@@ -382,6 +393,8 @@ ao_btm(void)
        /* Turn off status reporting */
        ao_btm_cmd("ATQ1\r");
 
+       ao_btm_drain();
+
        ao_btm_stdio = ao_add_stdio(_ao_serial_btm_pollchar,
                                    ao_serial_btm_putchar,
                                    NULL);
@@ -390,10 +403,6 @@ ao_btm(void)
        /* Check current pin state */
        ao_btm_check_link();
 
-#ifdef AO_BTM_INT_PORT
-       ao_exti_enable(AO_BTM_INT_PORT, AO_BTM_INT_PIN);
-#endif
-
        for (;;) {
                while (!ao_btm_connected)
                        ao_sleep(&ao_btm_connected);
index 90d6cc75ab5f76eb5677a052203a44e85eb6b94d..5b814667fccf8f18e67d9e7aec557489bfae915a 100644 (file)
@@ -837,15 +837,11 @@ ao_radio_test_cmd(void)
 static void
 ao_radio_wait_isr(uint16_t timeout)
 {
-       if (timeout)
-               ao_alarm(timeout);
        ao_arch_block_interrupts();
        while (!ao_radio_wake && !ao_radio_mcu_wake && !ao_radio_abort)
-               if (ao_sleep(&ao_radio_wake))
+               if (ao_sleep_for(&ao_radio_wake, timeout))
                        ao_radio_abort = 1;
        ao_arch_release_interrupts();
-       if (timeout)
-               ao_clear_alarm();
        if (ao_radio_mcu_wake)
                ao_radio_check_marc_status();
 }
@@ -1060,19 +1056,17 @@ ao_radio_rx_isr(void)
 static uint16_t
 ao_radio_rx_wait(void)
 {
-       ao_alarm(AO_MS_TO_TICKS(100));
        ao_arch_block_interrupts();
        rx_waiting = 1;
        while (rx_data_cur - rx_data_consumed < AO_FEC_DECODE_BLOCK &&
               !ao_radio_abort &&
               !ao_radio_mcu_wake)
        {
-               if (ao_sleep(&ao_radio_wake))
+               if (ao_sleep_for(&ao_radio_wake, AO_MS_TO_TICKS(100)))
                        ao_radio_abort = 1;
        }
        rx_waiting = 0;
        ao_arch_release_interrupts();
-       ao_clear_alarm();
        if (ao_radio_abort || ao_radio_mcu_wake)
                return 0;
        rx_data_consumed += AO_FEC_DECODE_BLOCK;
@@ -1133,19 +1127,15 @@ ao_radio_recv(__xdata void *d, uint8_t size, uint8_t timeout)
 
        ao_radio_strobe(CC1120_SRX);
 
-       if (timeout)
-               ao_alarm(timeout);
        ao_arch_block_interrupts();
        while (rx_starting && !ao_radio_abort) {
-               if (ao_sleep(&ao_radio_wake))
+               if (ao_sleep_for(&ao_radio_wake, timeout))
                        ao_radio_abort = 1;
        }
        uint8_t rx_task_id_save = rx_task_id;
        rx_task_id = 0;
        rx_starting = 0;
        ao_arch_release_interrupts();
-       if (timeout)
-               ao_clear_alarm();
 
        if (ao_radio_abort) {
                if (rx_task_id_save == 0)
index 8546900e830246650b49e31147abf05a20783707..6547be3963d910055a8628aeb11b52b029cbc376 100644 (file)
@@ -41,7 +41,11 @@ int8_t       ao_radio_rssi;                  /* Last received RSSI value */
 
 extern const uint32_t  ao_radio_cal;
 
+#ifdef AO_CC1200_FOSC
+#define FOSC   AO_CC1200_FOSC
+#else
 #define FOSC   40000000
+#endif
 
 #define ao_radio_select()      ao_spi_get_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS,AO_SPI_SPEED_FAST)
 #define ao_radio_deselect()    ao_spi_put_mask(AO_CC1200_SPI_CS_PORT,(1 << AO_CC1200_SPI_CS_PIN),AO_CC1200_SPI_BUS)
@@ -301,20 +305,28 @@ ao_radio_idle(void)
  *     CHANBW = 5.0 (round to 9.5)
  */
 
+#if FOSC == 40000000
 #define PACKET_SYMBOL_RATE_M           1013008
-
 #define PACKET_SYMBOL_RATE_E_384       8
+#define PACKET_SYMBOL_RATE_E_96                6
+#define PACKET_SYMBOL_RATE_E_24                4
+#endif
+
+#if FOSC == 32000000
+#define PACKET_SYMBOL_RATE_M           239914
+#define PACKET_SYMBOL_RATE_E_384       9
+#define PACKET_SYMBOL_RATE_E_96                7
+#define PACKET_SYMBOL_RATE_E_24                5
+#endif
 
 /* 200 / 2 = 100 */
 #define PACKET_CHAN_BW_384     ((CC1200_CHAN_BW_ADC_CIC_DECFACT_12 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
                                 (16 << CC1200_CHAN_BW_BB_CIC_DECFACT))
 
-#define PACKET_SYMBOL_RATE_E_96                6
 /* 200 / 10 = 20 */
 #define PACKET_CHAN_BW_96      ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
                                 (16 << CC1200_CHAN_BW_BB_CIC_DECFACT))
 
-#define PACKET_SYMBOL_RATE_E_24                4
 /* 200 / 25 = 8 */
 #define PACKET_CHAN_BW_24      ((CC1200_CHAN_BW_ADC_CIC_DECFACT_48 << CC1200_CHAN_BW_ADC_CIC_DECFACT) | \
                                 (44 << CC1200_CHAN_BW_BB_CIC_DECFACT))
@@ -715,17 +727,11 @@ ao_radio_show_state(char *where)
 static void
 ao_radio_wait_isr(uint16_t timeout)
 {
-       if (timeout)
-               ao_alarm(timeout);
-
        ao_arch_block_interrupts();
        while (!ao_radio_wake && !ao_radio_abort)
-               if (ao_sleep(&ao_radio_wake))
+               if (ao_sleep_for(&ao_radio_wake, timeout))
                        ao_radio_abort = 1;
        ao_arch_release_interrupts();
-
-       if (timeout)
-               ao_clear_alarm();
 }
 
 static void
index b04775fdc964f87d0fdb6794e79245c14a4f932d..b2b63cde490a1b5c294e64111ed7c2bf39aeee88 100644 (file)
 #define CC1200_IF_MIX_CFG      (CC1200_EXTENDED_BIT | 0x00)
 #define CC1200_FREQOFF_CFG     (CC1200_EXTENDED_BIT | 0x01)
 #define CC1200_TOC_CFG         (CC1200_EXTENDED_BIT | 0x02)
+
+#define CC1200_TOC_CFG_TOC_LIMIT               6
+#define  CC1200_TOC_CFG_TOC_LIMIT_0_2                  0
+#define  CC1200_TOC_CFG_TOC_LIMIT_2                    1
+#define  CC1200_TOC_CFG_TOC_LIMIT_12                   3
+
+#define CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN   3
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_8                        0
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_16               1
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_32               2
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_64               3
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_128              4
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_256              5
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_8_16             0
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_6_16             1
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_2_16             2
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_1_16             3
+#define  CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_1_16_SYNC                4
+
+#define CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN  0
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_8               0
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_16              1
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_32              2
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_64              3
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_128             4
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_256             5
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_FREEZE          0
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_6_32            1
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_2_32            2
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_1_32            3
+#define  CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_1_32_SYNC       4
+
 #define CC1200_MARC_SPARE      (CC1200_EXTENDED_BIT | 0x03)
 #define CC1200_ECG_CFG         (CC1200_EXTENDED_BIT | 0x04)
 #define CC1200_MDMCFG2         (CC1200_EXTENDED_BIT | 0x05)
index 3567312380b5c1aadc8a2107f93cd9a8b64c0f3b..f0214c2a25df1f8f80b244290baa59b92ede99e3 100644 (file)
                 (CC1200_MDMCFG2_SYMBOL_MAP_CFG_MODE_0 << CC1200_MDMCFG2_SYMBOL_MAP_CFG) |
                 (CC1200_MDMCFG2_UPSAMPLER_P_8 << CC1200_MDMCFG2_UPSAMPLER_P) |
                 (0 << CC1200_MDMCFG2_CFM_DATA_EN)),
+       CC1200_MDMCFG0,                                  /* General Modem Parameter Configuration Reg. 0 */ 
+               ((0 << CC1200_MDMCFG0_TRANSPARENT_MODE_EN) |
+                (0 << CC1200_MDMCFG0_TRANSPARENT_INTFACT) |
+                (0 << CC1200_MDMCFG0_DATA_FILTER_EN) | 
+                (1 << CC1200_MDMCFG0_VITERBI_EN)),
+       CC1200_TOC_CFG,                                 /* Timing Offset Correction Configuration */
+               ((CC1200_TOC_CFG_TOC_LIMIT_2 << CC1200_TOC_CFG_TOC_LIMIT) |
+                (CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN_6_16 << CC1200_TOC_CFG_TOC_PRE_SYNC_BLOCKLEN)|
+                (CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN_2_32 << CC1200_TOC_CFG_TOC_POST_SYNC_BLOCKLEN)),
         CC1200_FREQ2,                        0x6c,       /* Frequency Configuration [23:16] */
         CC1200_FREQ1,                        0xa3,       /* Frequency Configuration [15:8] */
         CC1200_FREQ0,                        0x33,       /* Frequency Configuration [7:0] */
index 570b9e409bbdd7f1245900d383a344d8a4b53a67..7e02939bf7bc0c01bca788bce526da7ee1172115 100644 (file)
@@ -102,8 +102,7 @@ ao_companion(void)
                    break;
        }
        while (ao_companion_running) {
-               ao_alarm(ao_companion_setup.update_period);
-               if (ao_sleep(DATA_TO_XDATA(&ao_flight_state)))
+               if (ao_sleep_for(DATA_TO_XDATA(&ao_flight_state), ao_companion_setup.update_period))
                        ao_companion_get_data();
                else
                        ao_companion_notify();
index 2d217bcf9d392fbd70099beaa3f21eee6ff8f871..f761671a1d3e0dac1cbbe90be7a0408e6ae928d0 100644 (file)
@@ -75,13 +75,11 @@ ao_hmc5883_sample(struct ao_hmc5883_sample *sample)
        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));
        ao_arch_block_interrupts();
        while (!ao_hmc5883_done)
-               if (ao_sleep(&ao_hmc5883_done))
+               if (ao_sleep_for(&ao_hmc5883_done, AO_MS_TO_TICKS(10)))
                        ++ao_hmc5883_missed_irq;
        ao_arch_release_interrupts();
-       ao_clear_alarm();
 
        ao_hmc5883_read(HMC5883_X_MSB, (uint8_t *) sample, sizeof (struct ao_hmc5883_sample));
 #if __BYTE_ORDER == __LITTLE_ENDIAN
diff --git a/src/drivers/ao_lco.c b/src/drivers/ao_lco.c
new file mode 100644 (file)
index 0000000..b8698a8
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * 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_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 uint8_t ao_lco_got_channels;
+static uint16_t        ao_lco_tick_offset;
+
+static struct ao_pad_query     ao_pad_query;
+
+static void
+ao_lco_set_pad(uint8_t pad)
+{
+       ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad);
+}
+
+static void
+ao_lco_set_box(uint8_t box)
+{
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10);
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, box / 10);
+}
+
+static void
+ao_lco_set_voltage(uint16_t decivolts)
+{
+       uint8_t tens, ones, tenths;
+
+       tenths = decivolts % 10;
+       ones = (decivolts / 10) % 10;
+       tens = (decivolts / 100) % 10;
+       ao_seven_segment_set(AO_LCO_PAD_DIGIT, tenths);
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, ones | 0x10);
+       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, tens);
+}
+
+static void
+ao_lco_set_display(void)
+{
+       if (ao_lco_pad == 0) {
+               ao_lco_set_voltage(ao_pad_query.battery);
+       } else {
+               ao_lco_set_pad(ao_lco_pad);
+               ao_lco_set_box(ao_lco_box);
+       }
+}
+
+#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 uint8_t
+ao_lco_pad_present(uint8_t pad)
+{
+       if (!ao_lco_got_channels || !ao_pad_query.channels)
+               return pad == 0;
+       /* voltage measurement is always valid */
+       if (pad == 0)
+               return 1;
+       if (pad > AO_PAD_MAX_CHANNELS)
+               return 0;
+       return (ao_pad_query.channels >> (pad - 1)) & 1;
+}
+
+static uint8_t
+ao_lco_pad_first(void)
+{
+       uint8_t pad;
+
+       for (pad = 1; pad <= AO_PAD_MAX_CHANNELS; pad++)
+               if (ao_lco_pad_present(pad))
+                       return pad;
+       return 0;
+}
+
+static void
+ao_lco_input(void)
+{
+       static struct ao_event  event;
+       int8_t  dir, new_box, new_pad;
+
+       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
+       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) {
+                                       dir = (int8_t) event.value;
+                                       new_pad = ao_lco_pad;
+                                       do {
+                                               new_pad += dir;
+                                               if (new_pad > AO_PAD_MAX_CHANNELS)
+                                                       new_pad = 0;
+                                               if (new_pad < 0)
+                                                       new_pad = AO_PAD_MAX_CHANNELS;
+                                               if (new_pad == ao_lco_pad)
+                                                       break;
+                                       } while (!ao_lco_pad_present(new_pad));
+                                       if (new_pad != ao_lco_pad) {
+                                               ao_lco_pad = new_pad;
+                                               ao_lco_set_display();
+                                       }
+                               }
+                               break;
+                       case AO_QUADRATURE_BOX:
+                               if (!ao_lco_armed) {
+                                       dir = (int8_t) event.value;
+                                       new_box = ao_lco_box;
+                                       do {
+                                               new_box += dir;
+                                               if (new_box > ao_lco_max_box)
+                                                       new_box = ao_lco_min_box;
+                                               else if (new_box < ao_lco_min_box)
+                                                       new_box = ao_lco_max_box;
+                                               if (new_box == ao_lco_box)
+                                                       break;
+                                       } while (!ao_lco_box_present(new_box));
+                                       if (ao_lco_box != new_box) {
+                                               ao_lco_box = new_box;
+                                               ao_lco_pad = 1;
+                                               ao_lco_got_channels = 0;
+                                               ao_lco_set_display();
+                                       }
+                               }
+                               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 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) {
+               c = ao_lco_got_channels;
+               ao_lco_got_channels = 1;
+               ao_lco_valid = 1;
+               if (!c) {
+                       if (ao_lco_pad != 0)
+                               ao_lco_pad = ao_lco_pad_first();
+                       ao_lco_set_display();
+               }
+               if (ao_lco_pad == 0)
+                       ao_lco_set_display();
+       } else
+               ao_lco_valid = 0;
+
+#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
+       PRINTD("ao_lco_update valid %d\n", ao_lco_valid);
+       ao_wakeup(&ao_pad_query);
+}
+
+static void
+ao_lco_box_reset_present(void)
+{
+       ao_lco_min_box = 0xff;
+       ao_lco_max_box = 0x00;
+       memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
+}
+
+static void
+ao_lco_box_set_present(uint8_t box)
+{
+       if (box < ao_lco_min_box)
+               ao_lco_min_box = box;
+       if (box > ao_lco_max_box)
+               ao_lco_max_box = 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;
+       int8_t          try;
+       uint8_t         box;
+       uint8_t         boxes = 0;
+
+       ao_lco_box_reset_present();
+       ao_lco_set_pad(0);
+       for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
+               if ((box % 10) == 0)
+                       ao_lco_set_box(box);
+               for (try = 0; try < 3; try++) {
+                       tick_offset = 0;
+                       r = ao_lco_query(box, &ao_pad_query, &tick_offset);
+                       PRINTD("box %d result %d\n", box, r);
+                       if (r == AO_RADIO_CMAC_OK) {
+                               ++boxes;
+                               ao_lco_box_set_present(box);
+                               ao_lco_set_pad(boxes % 10);
+                               ao_delay(AO_MS_TO_TICKS(30));
+                               break;
+                       }
+               }
+       }
+       if (ao_lco_min_box <= ao_lco_max_box)
+               ao_lco_box = ao_lco_min_box;
+       else
+               ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
+       ao_lco_valid = 0;
+       ao_lco_got_channels = 0;
+       ao_lco_pad = 1;
+       ao_lco_set_display();
+}
+
+static void
+ao_lco_igniter_status(void)
+{
+       uint8_t         c;
+
+       for (;;) {
+               ao_sleep(&ao_pad_query);
+               PRINTD("RSSI %d VALID %d\n", ao_radio_cmac_rssi, ao_lco_valid);
+               if (!ao_lco_valid) {
+                       ao_led_on(AO_LED_RED);
+                       ao_led_off(AO_LED_GREEN|AO_LED_AMBER);
+                       continue;
+               }
+               if (ao_radio_cmac_rssi < -90) {
+                       ao_led_on(AO_LED_AMBER);
+                       ao_led_off(AO_LED_RED|AO_LED_GREEN);
+               } else {
+                       ao_led_on(AO_LED_GREEN);
+                       ao_led_off(AO_LED_RED|AO_LED_AMBER);
+               }
+               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_pad)
+                               ao_lco_ignite(ao_lco_box, 1 << (ao_lco_pad - 1), 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();
+                       if (ao_lco_pad) {
+                               ao_lco_arm(ao_lco_box, 1 << (ao_lco_pad - 1), ao_lco_tick_offset);
+                               ao_delay(AO_MS_TO_TICKS(30));
+                               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_sleep_for(&ao_lco_armed, delay);
+       }
+}
+
+#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" },
+       { ao_lco_search,        "s\0Search for pad boxes" },
+       { 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/drivers/ao_lco.h b/src/drivers/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_ */
index 8cdf85a94f9036648acf1332bfc94cb158567afd..18330eadbec53deb7faf4f8468227f5cdc494bd7 100644 (file)
@@ -54,14 +54,14 @@ ao_packet_send(void)
 }
 
 uint8_t
-ao_packet_recv(void)
+ao_packet_recv(uint16_t timeout)
 {
        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), 0);
+       dma_done = ao_radio_recv(&ao_rx_packet, sizeof (struct ao_packet_recv), timeout);
 #ifdef AO_LED_GREEN
        ao_led_off(AO_LED_GREEN);
 #endif
index 42a4f5bfd35f064f22aa9cd682aa5295a897fe3b..5e440db04776442dc0c516bdcdadb69f4ed8ce12 100644 (file)
@@ -97,9 +97,7 @@ ao_packet_master(void)
                if (ao_tx_packet.len)
                        ao_packet_master_busy();
                ao_packet_master_check_busy();
-               ao_alarm(AO_PACKET_MASTER_RECV_DELAY);
-               r = ao_packet_recv();
-               ao_clear_alarm();
+               r = ao_packet_recv(AO_PACKET_MASTER_RECV_DELAY);
                if (r) {
                        /* if we can transmit data, do so */
                        if (ao_packet_tx_used && ao_tx_packet.len == 0)
@@ -107,9 +105,7 @@ ao_packet_master(void)
                        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_sleep_for(&ao_packet_master_sleeping, ao_packet_master_delay);
                        ao_packet_master_sleeping = 0;
                }
        }
index e75df0d619fb92509d0a76011757cb7e8b2d75b4..0872682fa1c307b237a7f6b82b39422f942270d7 100644 (file)
@@ -24,7 +24,7 @@ ao_packet_slave(void)
        ao_tx_packet.len = AO_PACKET_SYN;
        ao_packet_restart = 1;
        while (ao_packet_enable) {
-               if (ao_packet_recv()) {
+               if (ao_packet_recv(0)) {
                        ao_xmemcpy(&ao_tx_packet.callsign, &ao_rx_packet.packet.callsign, AO_MAX_CALLSIGN);
 #if HAS_FLIGHT
                        ao_flight_force_idle = TRUE;
index dc2c83fe0cf3c668668dfbf354cbae020d14533f..ffe46c6833723df2279559690e1bec2e7ad6272e 100644 (file)
@@ -29,6 +29,10 @@ static __pdata uint8_t       ao_pad_box;
 static __xdata uint8_t ao_pad_disabled;
 static __pdata uint16_t        ao_pad_packet_time;
 
+#ifndef AO_PAD_RSSI_MINIMUM
+#define AO_PAD_RSSI_MINIMUM    -90
+#endif
+
 #define DEBUG  1
 
 #if DEBUG
@@ -36,8 +40,8 @@ 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()    
+#define PRINTD(...)
+#define FLUSHD()
 #endif
 
 static void
@@ -123,6 +127,8 @@ ao_pad_monitor(void)
 
 #define VOLTS_TO_PYRO(x) ((int16_t) ((x) * 27.0 / 127.0 / 3.3 * 32767.0))
 
+               /* convert ADC value to voltage in tenths, then add .2 for the diode drop */
+               query.battery = (packet->adc.batt + 96) / 192 + 2;
                cur = 0;
                if (pyro > VOLTS_TO_PYRO(10)) {
                        query.arm_status = AO_PAD_ARM_STATUS_ARMED;
@@ -138,7 +144,7 @@ ao_pad_monitor(void)
                }
                if ((ao_time() - ao_pad_packet_time) > AO_SEC_TO_TICKS(2))
                        cur |= AO_LED_RED;
-               else if (ao_radio_cmac_rssi < -90)
+               else if (ao_radio_cmac_rssi < AO_PAD_RSSI_MINIMUM)
                        cur |= AO_LED_AMBER;
                else
                        cur |= AO_LED_GREEN;
@@ -255,7 +261,7 @@ ao_pad(void)
                if (ret != AO_RADIO_CMAC_OK)
                        continue;
                ao_pad_packet_time = ao_time();
-               
+
                ao_pad_box = ao_pad_read_box();
 
                PRINTD ("tick %d box %d (me %d) cmd %d channels %02x\n",
index 23062899bdf29fd0e1140cc3d7f9931e2874ac69..d77d105a524d5aa41da53b10216dc932e545d406 100644 (file)
@@ -39,6 +39,7 @@ struct ao_pad_query {
        uint8_t         channels;       /* which chanels are present */
        uint8_t         armed;          /* which channels are armed */
        uint8_t         arm_status;     /* status of arming switch */
+       uint8_t         battery;        /* battery voltage in decivolts */
        uint8_t         igniter_status[AO_PAD_MAX_CHANNELS];    /* status for each igniter */
 };
 
diff --git a/src/drivers/ao_trng.c b/src/drivers/ao_trng.c
new file mode 100644 (file)
index 0000000..e69cd30
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 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_adc_fast.h>
+#include <ao_crc.h>
+#include <ao_trng.h>
+
+static void
+ao_trng_fetch(void)
+{
+       static uint16_t *buffer[2];
+       uint32_t        kbytes = 1;
+       uint32_t        count;
+       int             usb_buf_id;
+       uint16_t        i;
+       uint16_t        *buf;
+       uint16_t        t;
+       uint32_t        *rnd = (uint32_t *) ao_adc_ring;
+
+       if (!buffer[0]) {
+               buffer[0] = ao_usb_alloc();
+               buffer[1] = ao_usb_alloc();
+               if (!buffer[0])
+                       return;
+       }
+
+       ao_cmd_decimal();
+       if (ao_cmd_status == ao_cmd_success)
+               kbytes = ao_cmd_lex_u32;
+       else
+               ao_cmd_status = ao_cmd_success;
+       usb_buf_id = 0;
+       count = kbytes * (1024/AO_USB_IN_SIZE);
+
+       ao_crc_reset();
+
+       ao_led_on(AO_LED_TRNG_READ);
+       while (count--) {
+               t = ao_adc_get(AO_USB_IN_SIZE) >> 1;    /* one 16-bit value per output byte */
+               buf = buffer[usb_buf_id];
+               for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
+                       *buf++ = ao_crc_in_32_out_16(rnd[t]);
+                       t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
+               }
+               ao_adc_ack(AO_USB_IN_SIZE);
+               ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
+               ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
+               ao_led_toggle(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
+               usb_buf_id = 1-usb_buf_id;
+       }
+       ao_led_off(AO_LED_TRNG_READ|AO_LED_TRNG_WRITE);
+       flush();
+}
+
+static const struct ao_cmds ao_trng_cmds[] = {
+       { ao_trng_fetch,        "f <kbytes>\0Fetch a block of numbers" },
+       { 0, NULL },
+};
+
+void
+ao_trng_init(void)
+{
+       ao_cmd_register(ao_trng_cmds);
+}
diff --git a/src/drivers/ao_trng.h b/src/drivers/ao_trng.h
new file mode 100644 (file)
index 0000000..7857742
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_TRNG_H_
+#define _AO_TRNG_H_
+
+void
+ao_trng_init(void);
+
+#endif /* _AO_TRNG_H_ */
diff --git a/src/drivers/ao_trng_send.c b/src/drivers/ao_trng_send.c
new file mode 100644 (file)
index 0000000..bac6035
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 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_adc_fast.h>
+#include <ao_crc.h>
+#include <ao_trng_send.h>
+
+static void
+ao_trng_send(void)
+{
+       static uint16_t *buffer[2];
+       int             usb_buf_id;
+       uint16_t        i;
+       uint16_t        *buf;
+       uint16_t        t;
+       uint32_t        *rnd = (uint32_t *) ao_adc_ring;
+
+       if (!buffer[0]) {
+               buffer[0] = ao_usb_alloc();
+               buffer[1] = ao_usb_alloc();
+               if (!buffer[0])
+                       return;
+       }
+
+       usb_buf_id = 0;
+
+       ao_crc_reset();
+
+       for (;;) {
+               ao_led_on(AO_LED_TRNG_ACTIVE);
+               t = ao_adc_get(AO_USB_IN_SIZE) >> 1;    /* one 16-bit value per output byte */
+               buf = buffer[usb_buf_id];
+               for (i = 0; i < AO_USB_IN_SIZE / sizeof (uint16_t); i++) {
+                       *buf++ = ao_crc_in_32_out_16(rnd[t]);
+                       t = (t + 1) & ((AO_ADC_RING_SIZE>>1) - 1);
+               }
+               ao_adc_ack(AO_USB_IN_SIZE);
+               ao_led_off(AO_LED_TRNG_ACTIVE);
+               ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
+               usb_buf_id = 1-usb_buf_id;
+       }
+}
+
+static struct ao_task ao_trng_send_task;
+
+void
+ao_trng_send_init(void)
+{
+       ao_add_task(&ao_trng_send_task, ao_trng_send, "trng_send");
+}
diff --git a/src/drivers/ao_trng_send.h b/src/drivers/ao_trng_send.h
new file mode 100644 (file)
index 0000000..83312d5
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_TRNG_SEND_H_
+#define _AO_TRNG_SEND_H_
+
+void
+ao_trng_send_init(void);
+
+#endif /* _AO_TRNG_SEND_H_ */
index 8dab7c42ad4ae06366f1364d1e7013aca1fd75a2..b0d3e5417533ccf32e79e8fd905fc5b386d1d7b7 100644 (file)
@@ -219,6 +219,10 @@ _ao_config_get(void)
 #if HAS_RADIO_FORWARD
                if (minor < 21)
                        ao_config.send_frequency = 434550;
+#endif
+#if HAS_APRS
+               if (minor < 22)
+                       ao_config.aprs_format = AO_CONFIG_DEFAULT_APRS_FORMAT;
 #endif
                ao_config.minor = AO_CONFIG_MINOR;
                ao_config_dirty = 1;
@@ -876,6 +880,23 @@ ao_config_aprs_ssid_set(void)
        ao_config.aprs_ssid = ao_cmd_lex_i;
        _ao_config_edit_finish();
 }
+
+void
+ao_config_aprs_format_set(void)
+{
+       ao_cmd_decimal();
+       if (ao_cmd_status != ao_cmd_success)
+               return;
+       _ao_config_edit_start();
+       ao_config.aprs_format = ao_cmd_lex_i != 0;
+       _ao_config_edit_finish();
+}
+
+void
+ao_config_aprs_format_show(void)
+{
+       printf ("APRS format: %d\n", ao_config.aprs_format);
+}
 #endif /* HAS_APRS */
 
 struct ao_config_var {
@@ -969,6 +990,8 @@ __code struct ao_config_var ao_config_vars[] = {
 #if HAS_APRS
        { "S <ssid>\0Set APRS SSID (0-15)",
          ao_config_aprs_ssid_set, ao_config_aprs_ssid_show },
+       { "C <0 compressed, 1 uncompressed>\0APRS format",
+         ao_config_aprs_format_set, ao_config_aprs_format_show },
 #endif
        { "s\0Show",
          ao_config_show,               0 },
index 164584a53fcf042725f00be1488101644e34ae83..cfe8555c0387af784c747f9b57dd0df499f76294 100644 (file)
@@ -57,7 +57,7 @@
 #endif
 
 #define AO_CONFIG_MAJOR        1
-#define AO_CONFIG_MINOR        21
+#define AO_CONFIG_MINOR        22
 
 #define AO_AES_LEN 16
 
@@ -115,8 +115,15 @@ struct ao_config {
 #if HAS_RADIO_FORWARD
        uint32_t        send_frequency;         /* minor version 21 */
 #endif
+#if HAS_APRS
+       uint8_t         aprs_format;            /* minor version 22 */
+#endif
 };
 
+#define AO_APRS_FORMAT_COMPRESSED      0
+#define AO_APRS_FORMAT_UNCOMPRESSED    1
+#define AO_CONFIG_DEFAULT_APRS_FORMAT  AO_APRS_FORMAT_COMPRESSED
+
 #if HAS_RADIO_FORWARD
 extern __xdata uint32_t        ao_send_radio_setting;
 #endif
index b8426cf90d7d8cdd7397dfe406bbbdf344cdd626..136609c3b0865c7dbc3772f2997cadc3acb173bf 100644 (file)
@@ -54,7 +54,7 @@ void
 ao_packet_send(void);
 
 uint8_t
-ao_packet_recv(void);
+ao_packet_recv(uint16_t timeout);
 
 void
 ao_packet_flush(void);
index b9327bacbf8b4a9543775c335cefd63ab97becfc..c711a4d261bec9685fc210579a129e7f6bb77668 100644 (file)
@@ -33,6 +33,10 @@ const char ao_product[] = AO_iProduct_STRING;
 #define AO_USB_MAX_POWER       100
 #endif
 
+#ifndef AO_USB_INTERFACE_CLASS
+#define AO_USB_INTERFACE_CLASS 0x02
+#endif
+
 #include "ao_usb.h"
 /* USB descriptors in one giant block of bytes */
 AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =
@@ -45,7 +49,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =
        0x00,                   /*  bDeviceSubClass */
        0x00,                   /*  bDeviceProtocol */
        AO_USB_CONTROL_SIZE,    /*  bMaxPacketSize */
-       LE_WORD(0xFFFE),        /*  idVendor */
+       LE_WORD(AO_idVendor_NUMBER),    /*  idVendor */
        LE_WORD(AO_idProduct_NUMBER),   /*  idProduct */
        LE_WORD(0x0100),        /*  bcdDevice */
        0x01,                   /*  iManufacturer */
@@ -69,7 +73,7 @@ AO_ROMCONFIG_SYMBOL(0x00aa) uint8_t ao_usb_descriptors [] =
        0x00,                   /*  bInterfaceNumber */
        0x00,                   /*  bAlternateSetting */
        0x01,                   /*  bNumEndPoints */
-       0x02,                   /*  bInterfaceClass */
+       AO_USB_INTERFACE_CLASS, /*  bInterfaceClass */
        0x02,                   /*  bInterfaceSubClass */
        0x01,                   /*  bInterfaceProtocol, linux requires value of 1 for the cdc_acm module */
        0x00,                   /*  iInterface */
index 3044d56517aa06fceaf198fb31a4165e5d6b9370..43e73de4a897cfa97ee6cdc851f4c7ef373727f1 100644 (file)
@@ -375,9 +375,7 @@ ao_pyro(void)
                ao_sleep(&ao_flight_state);
 
        for (;;) {
-               ao_alarm(AO_MS_TO_TICKS(100));
-               ao_sleep(&ao_pyro_wakeup);
-               ao_clear_alarm();
+               ao_sleep_for(&ao_pyro_wakeup, AO_MS_TO_TICKS(100));
                if (ao_flight_state >= ao_flight_landed)
                        break;
                any_waiting = ao_pyro_check();
index bff848f6d249fd797bfd02198619e5f366fe68e8..b6835346f74ff492c73255e2e785355ca2001358 100644 (file)
@@ -91,7 +91,6 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant
                return AO_RADIO_CMAC_TIMEOUT;
        }
 
-       ao_radio_cmac_rssi = ao_radio_rssi;
        if (!(cmac_data[len + AO_CMAC_KEY_LEN +1] & AO_RADIO_STATUS_CRC_OK))
                return AO_RADIO_CMAC_CRC_ERROR;
 
@@ -114,13 +113,15 @@ radio_cmac_recv(uint8_t len, uint16_t timeout) __reentrant
        /* 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;
        }
 
+       ao_radio_cmac_rssi = ao_radio_rssi;
+
        return AO_RADIO_CMAC_OK;
 }
 
@@ -161,4 +162,3 @@ ao_radio_cmac_recv(__xdata void *packet, uint8_t len, uint16_t timeout) __reentr
        ao_mutex_put(&ao_radio_cmac_mutex);
        return i;
 }
-
index dbc9f8e4121c36ec0f4ed3c873b742f28b7122fd..e21643ac008f9da659cb72e64a9f316bb7934622 100644 (file)
@@ -35,7 +35,7 @@ int
 _ao_serial0_pollchar(void);
 
 uint8_t
-_ao_serial0_sleep(void);
+_ao_serial0_sleep_for(uint16_t timeout);
 
 void
 ao_serial0_putchar(char c);
@@ -58,7 +58,7 @@ int
 _ao_serial1_pollchar(void);
 
 uint8_t
-_ao_serial1_sleep(void);
+_ao_serial1_sleep_for(uint16_t timeout);
 
 void
 ao_serial1_putchar(char c);
@@ -81,7 +81,7 @@ int
 _ao_serial2_pollchar(void);
 
 uint8_t
-_ao_serial2_sleep(void);
+_ao_serial2_sleep_for(uint16_t timeout);
 
 void
 ao_serial2_putchar(char c);
@@ -104,7 +104,7 @@ int
 _ao_serial3_pollchar(void);
 
 uint8_t
-_ao_serial3_sleep(void);
+_ao_serial3_sleep_for(uint16_t timeout);
 
 void
 ao_serial3_putchar(char c);
index bafb49439d4441022be1b9159947f30a9f8d7bff..55e423bb90bae9589f926ddbb3aeae690e2ad48a 100644 (file)
@@ -450,37 +450,39 @@ ao_wakeup(__xdata void *wchan) __reentrant
        ao_check_stack();
 }
 
-void
-ao_alarm(uint16_t delay)
+uint8_t
+ao_sleep_for(__xdata void *wchan, uint16_t timeout)
 {
+       uint8_t ret;
+       if (timeout) {
 #if HAS_TASK_QUEUE
-       uint32_t flags;
-       /* 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
-        */
-       flags = ao_arch_irqsave();
+               uint32_t flags;
+               /* 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
+                */
+               flags = ao_arch_irqsave();
 #endif
-       if (!(ao_cur_task->alarm = ao_time() + delay + 1))
-               ao_cur_task->alarm = 1;
+               if (!(ao_cur_task->alarm = ao_time() + timeout + 1))
+                       ao_cur_task->alarm = 1;
 #if HAS_TASK_QUEUE
-       ao_task_to_alarm_queue(ao_cur_task);
-       ao_arch_irqrestore(flags);
+               ao_task_to_alarm_queue(ao_cur_task);
+               ao_arch_irqrestore(flags);
 #endif
-}
-
-void
-ao_clear_alarm(void)
-{
+       }
+       ret = ao_sleep(wchan);
+       if (timeout) {
 #if HAS_TASK_QUEUE
-       uint32_t flags;
+               uint32_t flags;
 
-       flags = ao_arch_irqsave();
+               flags = ao_arch_irqsave();
 #endif
-       ao_cur_task->alarm = 0;
+               ao_cur_task->alarm = 0;
 #if HAS_TASK_QUEUE
-       ao_task_from_alarm_queue(ao_cur_task);
-       ao_arch_irqrestore(flags);
+               ao_task_from_alarm_queue(ao_cur_task);
+               ao_arch_irqrestore(flags);
 #endif
+       }
+       return ret;
 }
 
 static __xdata uint8_t ao_forever;
@@ -488,9 +490,7 @@ static __xdata uint8_t ao_forever;
 void
 ao_delay(uint16_t ticks)
 {
-       ao_alarm(ticks);
-       ao_sleep(&ao_forever);
-       ao_clear_alarm();
+       ao_sleep_for(&ao_forever, ticks);
 }
 
 void
index 9c56b48021d6dd4ebe83c69f44aa04c5ade023c3..c6bec0e397dccba3f1625160330ff4af049f27fa 100644 (file)
@@ -68,10 +68,19 @@ extern __data uint8_t ao_task_minimize_latency;     /* Reduce IRQ latency */
 uint8_t
 ao_sleep(__xdata void *wchan);
 
+/* Suspend the current task until wchan is awoken or the timeout
+ * expires. returns:
+ *  0 on normal wake
+ *  1 on alarm
+ */
+uint8_t
+ao_sleep_for(__xdata void *wchan, uint16_t timeout);
+
 /* Wake all tasks sleeping on wchan */
 void
 ao_wakeup(__xdata void *wchan) __reentrant;
 
+#if 0
 /* set an alarm to go off in 'delay' ticks */
 void
 ao_alarm(uint16_t delay);
@@ -79,6 +88,7 @@ ao_alarm(uint16_t delay);
 /* Clear any pending alarm */
 void
 ao_clear_alarm(void);
+#endif
 
 /* Yield the processor to another task */
 void
index e2197f7a24cb1f9ad735ed49ea0d3c7c99cfb3c3..854ac898dcbc646613caf036050e7ce205c6e2a0 100644 (file)
@@ -486,9 +486,7 @@ ao_telemetry(void)
 #endif /* HAS_APRS */
                        delay = time - ao_time();
                        if (delay > 0) {
-                               ao_alarm(delay);
-                               ao_sleep(&telemetry);
-                               ao_clear_alarm();
+                               ao_sleep_for(&telemetry, delay);
                        }
                }
        }
index 711e0d36cb57230bb012c79265295c7763886582..672d23175f71598f8341bc4ff0cf217068215ef4 100644 (file)
@@ -258,13 +258,14 @@ struct ao_telemetry_metrum_data {
        uint16_t        serial;         /*  0 */
        uint16_t        tick;           /*  2 */
        uint8_t         type;           /*  4 */
+       uint8_t         pad5[3];        /*  5 */
 
-       int32_t         ground_pres;    /* 8 average pres on pad */
+       int32_t         ground_pres;    /*  8 average pres on pad */
        int16_t         ground_accel;   /* 12 average accel on pad */
        int16_t         accel_plus_g;   /* 14 accel calibration at +1g */
        int16_t         accel_minus_g;  /* 16 accel calibration at -1g */
 
-       uint8_t         pad[14];        /* 18 */
+       uint8_t         pad18[14];      /* 18 */
        /* 32 */
 };
 
@@ -332,6 +333,8 @@ union ao_telemetry_all {
        struct ao_telemetry_baro                baro;
 };
 
+typedef char ao_check_telemetry_size[sizeof(union ao_telemetry_all) == 32 ? 1 : -1];
+
 struct ao_telemetry_all_recv {
        union ao_telemetry_all          telemetry;
        int8_t                          rssi;
index 08236a15b17602c268981fcf8bcc752400c6cba3..c061559d38fcf3b048e6d50c90c5f61afc448c05 100644 (file)
@@ -8,13 +8,14 @@ LOADSLOW="%LOADSLOW%"
 LOADFAST=""
 
 case "$1" in
-fast)
-       LOADSPEED="$LOADFAST"
+slow)
+       LOADSPEED="$LOADSLOW"
        ;;
 *)
-       LOADSPEED="$LOADSLOW"
+       LOADSPEED="$LOADFAST"
        ;;
 esac
 
 echo ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
 ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
+/usr/games/xcowsay --cow-size=large --at=1000,500 "${HEX} finished"
index 5f6fe3b267be5c6b85cec1bdd52be797cd3303d7..c2062c3486699828780f94301c74a5da8457cce2 100644 (file)
@@ -1,2 +1,3 @@
 ao_product.h
-microsplash-*
+microsplash-v*
+microsplash-load
index 10cb825b590be501905314643072650ede6daaf8..9bb636f17622c516461d868b59fe7f972b10976d 100644 (file)
@@ -8,8 +8,15 @@ vpath make-altitude-pa ../util
 
 include ../avr/Makefile.defs
 
+PROGNAME=microsplash-v1.0
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SCRIPT=microsplash-load
+
 PUBLISH_DIR=$(HOME)/altusmetrumllc/Binaries
-PUBLISH_FILE=$(PUBLISH_DIR)/$(PROG)-$(VERSION).hex
+PUBLISH_HEX=$(PUBLISH_DIR)/$(HEX)
+PUBLISH_SCRIPT=$(PUBLISH_DIR)/$(SCRIPT)
 
 MCU=attiny85
 DUDECPUTYPE=t85
@@ -48,15 +55,13 @@ INC=\
        altitude-pa.h
 
 IDPRODUCT=0
-PRODUCT=MicroSplash-v0.1
+PRODUCT=MicroSplash-v1.0
 PRODUCT_DEF=-DMICROPEAK
 CFLAGS = $(PRODUCT_DEF) -I. -I../attiny -I../kernel -I.. -I../drivers -I../product
 CFLAGS += -g -mmcu=$(MCU) -Wall -Wstrict-prototypes -O2 -mcall-prologues -DATTINY
 
 NICKLE=nickle
 
-PROG=microsplash-v1.0
-
 SRC=$(ALTOS_SRC)
 OBJ=$(SRC:.c=.o)
 
@@ -68,7 +73,7 @@ endif
 # Otherwise, print the full command line.
 quiet ?= $($1)
 
-all: $(PROG) $(PROG).hex
+all: $(PROG) $(HEX) $(SCRIPT)
 
 CHECK=sh ../util/check-avr-mem
 
@@ -76,16 +81,16 @@ $(PROG): Makefile $(OBJ)
        $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ)
        $(call quiet,CHECK) $(PROG) || ($(RM) -f $(PROG); exit 1)
 
-$(PROG).hex: $(PROG)
+$(HEX): $(PROG)
        avr-size $(PROG)
        $(OBJCOPY) -R .eeprom -O ihex $(PROG) $@
 
 
-load: $(PROG).hex
-       $(LOADCMD) $(LOADARG)$(PROG).hex
+load: $(HEX)
+       $(LOADCMD) $(LOADARG)$(HEX)
 
-load-slow: $(PROG).hex
-       $(LOADCMD) $(LOADSLOW) $(LOADARG)$(PROG).hex
+load-slow: $(HEX)
+       $(LOADCMD) $(LOADSLOW) $(LOADARG)$(HEX)
 
 ao_product.h: ao-make-product.5c ../Version
        $(call quiet,NICKLE,$<) $< -m altusmetrum.org -i $(IDPRODUCT) -p $(PRODUCT) -v $(VERSION) > $@
@@ -98,22 +103,30 @@ ao_product.o: ao_product.c ao_product.h
 distclean:     clean
 
 clean:
-       rm -f *.o $(PROG) $(PROG).hex
+       rm -f *.o $(PROG) $(HEX) $(SCRIPT)
        rm -f ao_product.h
 
+publish: $(PUBLISH_HEX) $(PUBLISH_SCRIPT)
 
-publish: $(PROG).hex
-       cp -a $(PROG).hex $(PUBLISH_FILE)
+$(PUBLISH_HEX): $(HEX)
+       cp -a $(HEX) $@
+
+$(PUBLISH_SCRIPT): $(SCRIPT)
+       cp -a $(SCRIPT) $@
 
 load-product:
-       $(LOADCMD) $(LOADARG)$(PUBLISH_FILE)
+       ./$(SCRIPT) fast
 
 load-product-slow:
-       $(LOADCMD) $(LOADSLOW) $(LOADARG)$(PUBLISH_FILE)
+       ./$(SCRIPT) slow
 
 ../altitude-pa.h: make-altitude-pa
        nickle $< > $@
 
+$(SCRIPT): $(SCRIPT).tmpl Makefile ../Version
+       sed -e 's/%HEX%/$(HEX)/' -e 's/%LOADCMD%/$(LOADCMD)/' -e 's/%LOADARG%/$(LOADARG)/' -e 's/%LOADSLOW%/$(LOADSLOW)/' $(SCRIPT).tmpl > $@ || (rm $@ && exit 1)
+       chmod +x $@
+
 install:
 
 uninstall:
diff --git a/src/microsplash/microsplash-load.tmpl b/src/microsplash/microsplash-load.tmpl
new file mode 100644 (file)
index 0000000..c061559
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+dir=`dirname $0`
+
+HEX="$dir"/"%HEX%"
+LOADCMD="%LOADCMD%"
+LOADARG="%LOADARG%"
+LOADSLOW="%LOADSLOW%"
+LOADFAST=""
+
+case "$1" in
+slow)
+       LOADSPEED="$LOADSLOW"
+       ;;
+*)
+       LOADSPEED="$LOADFAST"
+       ;;
+esac
+
+echo ${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
+${LOADCMD} ${LOADSPEED} ${LOADARG}${HEX}
+/usr/games/xcowsay --cow-size=large --at=1000,500 "${HEX} finished"
index 8fd97033d6533329bd25af5d0e5ee3bb453f36ff..1e7b5dcd58511cd8a4dec4a915b7642e2e306dfd 100644 (file)
@@ -539,9 +539,7 @@ ao_terraui(void)
                else
                        ao_terraui_page[ao_current_page]();
 
-               ao_alarm(AO_SEC_TO_TICKS(1));
-               b = ao_button_get();
-               ao_clear_alarm();
+               b = ao_button_get(AO_SEC_TO_TICKS(1));
 
                if (b > 0) {
                        ao_beep_for(AO_BEEP_HIGH, AO_MS_TO_TICKS(10));
index 1c90cdb85c19193f223acaf40b970c9cfc74a267..158f5b218ebfe0edd877b4049322fb11b51f18fd 100644 (file)
@@ -195,15 +195,13 @@ ao_i2c_start(uint8_t index, uint16_t addr)
                if (!(stm_i2c->cr1 & (1 << STM_I2C_CR1_START)))
                        break;
        }
-       ao_alarm(AO_MS_TO_TICKS(250));
        ao_arch_block_interrupts();
        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]))
+               if (ao_sleep_for(&ao_i2c_state[index], AO_MS_TO_TICKS(250)))
                        break;
        ao_arch_release_interrupts();
-       ao_clear_alarm();
        return ao_i2c_state[index] == I2C_RUNNING;
 }
 
@@ -258,16 +256,14 @@ ao_i2c_send(void *block, uint16_t len, uint8_t index, uint8_t stop)
                            (STM_DMA_CCR_DIR_MEM_TO_PER << STM_DMA_CCR_DIR));
                           
        ao_dma_start(tx_dma_index);
-       ao_alarm(1 + len);
        ao_arch_block_interrupts();
        while (!ao_dma_done[tx_dma_index])
-               if (ao_sleep(&ao_dma_done[tx_dma_index]))
+               if (ao_sleep_for(&ao_dma_done[tx_dma_index], 1 + len))
                        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]))
+               if (ao_sleep_for(&ao_i2c_state[index], 1 + len))
                        break;
        stm_i2c->cr2 = AO_STM_I2C_CR2;
        ao_arch_release_interrupts();
@@ -321,14 +317,12 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
                if (stop)
                        stm_i2c->cr1 = AO_STM_I2C_CR1 | (1 << STM_I2C_CR1_STOP);
 
-               ao_alarm(1);
                ao_arch_block_interrupts();
                while (ao_i2c_recv_len[index])
-                       if (ao_sleep(&ao_i2c_recv_len[index]))
+                       if (ao_sleep_for(&ao_i2c_recv_len[index], 1))
                                break;
                ao_arch_release_interrupts();
                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,
@@ -351,13 +345,11 @@ ao_i2c_recv(void *block, uint16_t len, uint8_t index, uint8_t stop)
                ao_i2c_wait_addr(index);
 
                ao_dma_start(rx_dma_index);
-               ao_alarm(len);
                ao_arch_block_interrupts();
                while (!ao_dma_done[rx_dma_index])
-                       if (ao_sleep(&ao_dma_done[rx_dma_index]))
+                       if (ao_sleep_for(&ao_dma_done[rx_dma_index], len))
                                break;
                ao_arch_release_interrupts();
-               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);
index 2568cf430fe5cb92eccc751ae463d2e8c8ccf796..88f2d0299ecf65f1b350dde44e0ede1d744c7c1f 100644 (file)
@@ -86,9 +86,9 @@ ao_usart_getchar(struct ao_stm_usart *usart)
 }
 
 static inline uint8_t
-_ao_usart_sleep(struct ao_stm_usart *usart)
+_ao_usart_sleep_for(struct ao_stm_usart *usart, uint16_t timeout)
 {
-       return ao_sleep(&usart->rx_fifo);
+       return ao_sleep_for(&usart->rx_fifo, timeout);
 }
 
 void
@@ -217,9 +217,9 @@ _ao_serial1_pollchar(void)
 }
 
 uint8_t
-_ao_serial1_sleep(void)
+_ao_serial1_sleep_for(uint16_t timeout)
 {
-       return _ao_usart_sleep(&ao_stm_usart1);
+       return _ao_usart_sleep_for(&ao_stm_usart1, timeout);
 }
 
 void
@@ -260,9 +260,9 @@ _ao_serial2_pollchar(void)
 }
 
 uint8_t
-_ao_serial2_sleep(void)
+_ao_serial2_sleep_for(uint16_t timeout)
 {
-       return _ao_usart_sleep(&ao_stm_usart2);
+       return _ao_usart_sleep_for(&ao_stm_usart2, timeout);
 }
 
 void
@@ -303,9 +303,9 @@ _ao_serial3_pollchar(void)
 }
 
 uint8_t
-_ao_serial3_sleep(void)
+_ao_serial3_sleep_for(uint16_t timeout)
 {
-       return _ao_usart_sleep(&ao_stm_usart3);
+       return _ao_usart_sleep_for(&ao_stm_usart3, timeout);
 }
 
 void
index be9b5986778d51ff95310a1e72f2f9884bd33eb0..26e6691c914c87169d72905dbcdfe06ea393cca1 100644 (file)
 #include <ao.h>
 #include <ao_adc_fast.h>
 
-uint16_t ao_adc_ring[AO_ADC_RING_SIZE];
+uint16_t ao_adc_ring[AO_ADC_RING_SIZE] __attribute__((aligned(4)));
 
-uint16_t ao_adc_ring_head, ao_adc_ring_tail;
-uint8_t ao_adc_running;
+/* Maximum number of samples fetched per _ao_adc_start call */
+#define AO_ADC_RING_CHUNK      (AO_ADC_RING_SIZE >> 1)
+
+uint16_t ao_adc_ring_head, ao_adc_ring_remain;
+uint16_t ao_adc_running;
 
 /*
  * Callback from DMA ISR
  *
- * Mark time in ring, shut down DMA engine
+ * Wakeup any waiting processes, mark the DMA as done, start the ADC
+ * if there's still lots of space in the ring
  */
 static void ao_adc_dma_done(int index)
 {
        (void) index;
-       ao_adc_ring_head += AO_ADC_RING_CHUNK;
+       ao_adc_ring_head += ao_adc_running;
+       ao_adc_ring_remain += ao_adc_running;
        if (ao_adc_ring_head == AO_ADC_RING_SIZE)
                ao_adc_ring_head = 0;
        ao_adc_running = 0;
        ao_wakeup(&ao_adc_ring_head);
        ao_dma_done_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
+       _ao_adc_start();
 }
 
 void
 _ao_adc_start(void)
 {
        uint16_t        *buf;
+       uint16_t        count;
 
        if (ao_adc_running)
                return;
-       if (_ao_adc_space() < AO_ADC_RING_CHUNK)
+       count = _ao_adc_space();
+       if (count == 0)
                return;
-       ao_adc_running = 1;
+       if (count > AO_ADC_RING_CHUNK)
+               count = AO_ADC_RING_CHUNK;
+       ao_adc_running = count;
        buf = ao_adc_ring + ao_adc_ring_head;
        stm_adc.isr = 0;
        ao_dma_set_transfer(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1),
                            &stm_adc.dr,
                            buf,
-                           AO_ADC_RING_CHUNK,
+                           count,
                            (0 << STM_DMA_CCR_MEM2MEM) |
                            (STM_DMA_CCR_PL_HIGH << STM_DMA_CCR_PL) |
                            (STM_DMA_CCR_MSIZE_16 << STM_DMA_CCR_MSIZE) |
@@ -140,7 +150,6 @@ ao_adc_init(void)
 #if AO_NUM_ADC > 8
 #error Need more ADC defines
 #endif
-       stm_adc.chselr = chselr;
 
        /* Set the clock */
        stm_adc.cfgr2 = STM_ADC_CFGR2_CKMODE_PCLK_2 << STM_ADC_CFGR2_CKMODE;
@@ -160,14 +169,16 @@ ao_adc_init(void)
        while ((stm_adc.isr & (1 << STM_ADC_ISR_ADRDY)) == 0)
                ;
 
+       stm_adc.chselr = chselr;
+
        stm_adc.cfgr1 = ((0 << STM_ADC_CFGR1_AWDCH) |
                         (0 << STM_ADC_CFGR1_AWDEN) |
                         (0 << STM_ADC_CFGR1_AWDSGL) |
                         (0 << STM_ADC_CFGR1_DISCEN) |
                         (0 << STM_ADC_CFGR1_AUTOOFF) |
-                        (1 << STM_ADC_CFGR1_WAIT) |
+                        (0 << STM_ADC_CFGR1_WAIT) |
                         (1 << STM_ADC_CFGR1_CONT) |
-                        (0 << STM_ADC_CFGR1_OVRMOD) |
+                        (1 << STM_ADC_CFGR1_OVRMOD) |
                         (STM_ADC_CFGR1_EXTEN_DISABLE << STM_ADC_CFGR1_EXTEN) |
                         (0 << STM_ADC_CFGR1_ALIGN) |
                         (STM_ADC_CFGR1_RES_12 << STM_ADC_CFGR1_RES) |
@@ -186,5 +197,4 @@ ao_adc_init(void)
        stm_syscfg.cfgr1 &= ~(1 << STM_SYSCFG_CFGR1_ADC_DMA_RMP);
 
        ao_dma_alloc(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1));
-       ao_dma_set_isr(STM_DMA_INDEX(STM_DMA_CHANNEL_ADC_1), ao_adc_dma_done);
 }
index eec4550539390a75c14ee3e4ea90ced163d0bafe..c6903e9fcccb3b1d618eb29793bf861e87d6f659 100644 (file)
@@ -26,62 +26,59 @@ ao_adc_init(void);
 
 /* Total ring size in samples */
 #define AO_ADC_RING_SIZE       256
-/* Number of samples fetched per ao_adc_start call */
-#define AO_ADC_RING_CHUNK      (AO_ADC_RING_SIZE >> 1)
 
 extern uint16_t        ao_adc_ring[AO_ADC_RING_SIZE];
 
 #define ao_adc_ring_step(pos,inc)      (((pos) + (inc)) & (AO_ADC_RING_SIZE - 1))
 
-extern uint16_t        ao_adc_ring_head, ao_adc_ring_tail;
-extern uint8_t ao_adc_running;
-
-void
-_ao_adc_start(void);
+extern uint16_t        ao_adc_ring_head, ao_adc_ring_remain;
+extern uint16_t        ao_adc_running;
 
+/*
+ * Place to start fetching values from
+ */
 static inline uint16_t
-_ao_adc_remain(void)
+ao_adc_ring_tail(void)
 {
-       if (ao_adc_ring_tail > ao_adc_ring_head)
-               return AO_ADC_RING_SIZE - ao_adc_ring_tail;
-       return ao_adc_ring_head - ao_adc_ring_tail;
+       return (ao_adc_ring_head - ao_adc_ring_remain) & (AO_ADC_RING_SIZE - 1);
 }
 
+void
+_ao_adc_start(void);
+
+/*
+ * Space available to write ADC values into
+ */
 static inline uint16_t
 _ao_adc_space(void)
 {
-       if (ao_adc_ring_head == ao_adc_ring_tail)
-               return AO_ADC_RING_SIZE;
-       if (ao_adc_ring_head > ao_adc_ring_tail)
+       /* Free to end of buffer? */
+       if (ao_adc_ring_remain <= ao_adc_ring_head)
                return AO_ADC_RING_SIZE - ao_adc_ring_head;
-       return ao_adc_ring_tail - ao_adc_ring_head;
+
+       /* no, return just the unused entries beyond head */
+       return AO_ADC_RING_SIZE - ao_adc_ring_remain;
 }
 
-static inline uint16_t *
+static inline uint16_t
 ao_adc_get(uint16_t n)
 {
-       if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE)
-               ao_panic(AO_PANIC_ADC);
        ao_arch_block_interrupts();
-       while (_ao_adc_remain() < n) {
+       while (ao_adc_ring_remain < n) {
                if (!ao_adc_running)
                        _ao_adc_start();
                ao_sleep(&ao_adc_ring_head);
        }
        ao_arch_release_interrupts();
-       return &ao_adc_ring[ao_adc_ring_tail];
+       return ao_adc_ring_tail();
 }
 
 static inline void
 ao_adc_ack(uint16_t n)
 {
-       if (ao_adc_ring_tail + n > AO_ADC_RING_SIZE)
-               ao_panic(AO_PANIC_ADC);
        ao_arch_block_interrupts();
-       ao_adc_ring_tail += n;
-       if (ao_adc_ring_tail == AO_ADC_RING_SIZE)
-               ao_adc_ring_tail = 0;
-       if (!ao_adc_running && _ao_adc_space() >= AO_ADC_RING_CHUNK)
+       ao_adc_ring_remain -= n;
+       if (!ao_adc_running)
                _ao_adc_start();
        ao_arch_release_interrupts();
 }
diff --git a/src/stmf0/ao_crc_stm.c b/src/stmf0/ao_crc_stm.c
new file mode 100644 (file)
index 0000000..863f5ef
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright © 2015 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 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_crc.h>
+
+#ifndef AO_CRC_WIDTH
+#error "Must define AO_CRC_WIDTH"
+#endif
+
+/* Only the STM32F07x and ST32F09x series have
+ * programmable CRC units. Others can only do the ANSI CRC-32 computation
+ */
+
+#if !AO_HAVE_PROGRAMMABLE_CRC_UNIT && AO_CRC_WIDTH != 32
+#error "Target hardware does not have programmable CRC unit"
+#endif
+
+#ifndef AO_CRC_POLY
+#if AO_CRC_WIDTH == 16
+#define AO_CRC_POLY    AO_CRC_16_DEFAULT
+#endif
+#if AO_CRC_WIDTH == 32
+#define AO_CRC_POLY    AO_CRC_32_DEFAULT
+#endif
+#endif
+
+#if !AO_HAVE_PROGRAMMABLE_CRC_UNIT && (AO_CRC_WIDTH != 32 || AO_CRC_POLY != AO_CRC_32_ANSI)
+#error "Target hardware does not have programmable CRC unit"
+#endif
+
+#if AO_CRC_WIDTH == 32
+#define AO_CRC_CR_POLYSIZE     STM_CRC_CR_POLYSIZE_32
+#endif
+
+#if AO_CRC_WIDTH == 16
+#define AO_CRC_CR_POLYSIZE     STM_CRC_CR_POLYSIZE_16
+#endif
+
+#if AO_CRC_WIDTH == 8
+#define AO_CRC_CR_POLYSIZE     STM_CRC_CR_POLYSIZE_8
+#endif
+
+#if AO_CRC_WIDTH == 7
+#define AO_CRC_CR_POLYSIZE     STM_CRC_CR_POLYSIZE_7
+#endif
+
+#ifndef AO_CRC_INIT
+#define AO_CRC_INIT    0xffffffff
+#endif
+
+void
+ao_crc_reset(void)
+{
+       stm_crc.cr |= (1 << STM_CRC_CR_RESET);
+       while ((stm_crc.cr & (1 << STM_CRC_CR_RESET)) != 0)
+               ;
+}
+
+void
+ao_crc_init(void)
+{
+       /* Turn on the CRC clock */
+       stm_rcc.ahbenr |= (1 << STM_RCC_AHBENR_CRCEN);
+
+       /* Need to initialize CR even on non-programmable hardware,
+        * the write to the POLYSIZE bits will be ignored in that
+        * case
+        */
+       stm_crc.cr = (AO_CRC_CR_POLYSIZE << STM_CRC_CR_POLYSIZE);
+       stm_crc.init = AO_CRC_INIT;
+#if AO_HAVE_PROGRAMMABLE_CRC_UNIT
+       stm_crc.pol = AO_CRC_POLY;
+#endif
+       ao_crc_reset();
+}
diff --git a/src/stmf0/ao_exti.h b/src/stmf0/ao_exti.h
new file mode 100644 (file)
index 0000000..ebea224
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * 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
+#define AO_EXTI_PIN_NOCONFIGURE        64
+
+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_ */
index 3ea7da5e3caa030d5adcca5884b8ec06995a639a..b8146c216f1a2b0538e964dd8bc24862895d984c 100644 (file)
@@ -83,9 +83,13 @@ static uint16_t      ao_usb_sram_addr;
 static uint16_t        *ao_usb_ep0_tx_buffer;
 static uint16_t        *ao_usb_ep0_rx_buffer;
 
+/* Pointer to interrupt buffer in USB memory */
+static uint16_t ao_usb_int_tx_offset;
+
 /* Pointer to bulk data tx/rx buffers in USB memory */
 static uint16_t ao_usb_in_tx_offset;
 static uint16_t        *ao_usb_in_tx_buffer;
+static uint16_t ao_usb_out_rx_offset;
 static uint16_t        *ao_usb_out_rx_buffer;
 
 /* System ram shadow of USB buffer; writing individual bytes is
@@ -146,12 +150,10 @@ static inline uint16_t *ao_usb_packet_buffer_addr(uint16_t sram_addr)
        return (uint16_t *) (stm_usb_sram + sram_addr);
 }
 
-#if AO_USB_DIRECTIO
 static inline uint16_t ao_usb_packet_buffer_offset(uint16_t *addr)
 {
        return (uint16_t) ((uint8_t *) addr - stm_usb_sram);
 }
-#endif
 
 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;
@@ -323,27 +325,42 @@ ao_usb_init_ep(uint8_t ep, uint32_t addr, uint32_t type, uint32_t stat_rx, uint3
 }
 
 static void
-ao_usb_init_btable(void)
+ao_usb_alloc_buffers(void)
 {
        ao_usb_sram_addr = 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_int_tx_offset = ao_usb_sram_addr;
+       ao_usb_sram_addr += AO_USB_INT_SIZE;
+
+       ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_out_rx_offset = ao_usb_sram_addr;
+       ao_usb_sram_addr += AO_USB_OUT_SIZE;
+
+       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
+       ao_usb_in_tx_offset = ao_usb_sram_addr;
+       ao_usb_sram_addr += AO_USB_IN_SIZE;
+}
+
+static void
+ao_usb_init_btable(void)
+{
+       /* 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_packet_buffer_offset(ao_usb_ep0_tx_buffer);
+       ao_usb_bdt[0].single.count_tx = 0;
+
+       ao_usb_bdt[0].single.addr_rx = ao_usb_packet_buffer_offset(ao_usb_ep0_rx_buffer);
+       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));
 }
 
 static void
@@ -370,6 +387,8 @@ ao_usb_set_ep0(void)
        }
 
        ao_usb_set_address(0);
+
+       ao_usb_running = 0;
 }
 
 static void
@@ -378,9 +397,8 @@ ao_usb_set_configuration(void)
        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.addr_tx = ao_usb_int_tx_offset;
        ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0;
-       ao_usb_sram_addr += AO_USB_INT_SIZE;
 
        ao_usb_init_ep(AO_USB_INT_EPR,
                       AO_USB_INT_EP,
@@ -389,11 +407,9 @@ ao_usb_set_configuration(void)
                       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.addr_rx = ao_usb_out_rx_offset;
        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,
@@ -402,11 +418,8 @@ ao_usb_set_configuration(void)
                       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.addr_tx = ao_usb_in_tx_offset;
        ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0;
-       ao_usb_in_tx_offset = ao_usb_sram_addr;
-       ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_in_tx_offset);
-       ao_usb_sram_addr += AO_USB_IN_SIZE;
 
        ao_usb_init_ep(AO_USB_IN_EPR,
                       AO_USB_IN_EP,
@@ -415,6 +428,9 @@ ao_usb_set_configuration(void)
                       STM_USB_EPR_STAT_TX_NAK);
 
        ao_usb_running = 1;
+#if AO_USB_DIRECTIO
+       ao_wakeup(&ao_usb_running);
+#endif
 }
 
 static uint16_t        control_count;
@@ -916,8 +932,6 @@ ao_usb_alloc(void)
 {
        uint16_t        *buffer;
 
-       if (!ao_usb_running)
-               return NULL;
        buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr);
        ao_usb_sram_addr += AO_USB_IN_SIZE;
        return buffer;
@@ -936,12 +950,28 @@ ao_usb_write(uint16_t *buffer, uint16_t len)
 {
        ao_arch_block_interrupts();
 
-       /* Flush any pending regular */
-       if (ao_usb_tx_count)
-               _ao_usb_in_send();
+       /* Wait for everything to be ready at the same time */
+       for (;;) {
+               /* Make sure USB is connected */
+               if (!ao_usb_running) {
+                       ao_sleep(&ao_usb_running);
+                       continue;
+               }
+
+               /* Flush any pending regular I/O */
+               if (ao_usb_tx_count) {
+                       _ao_usb_in_send();
+                       continue;
+               }
+
+               /* Wait for an idle IN buffer */
+               if (ao_usb_in_pending) {
+                       ao_sleep(&ao_usb_in_pending);
+                       continue;
+               }
+               break;
+       }
 
-       while (ao_usb_in_pending)
-               ao_sleep(&ao_usb_in_pending);
        ao_usb_in_pending = 1;
        ao_usb_in_flushed = (len != AO_USB_IN_SIZE);
        ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_packet_buffer_offset(buffer);
@@ -1083,6 +1113,9 @@ ao_usb_init(void)
 
        debug ("ao_usb_init\n");
        ao_usb_ep0_state = AO_USB_EP0_IDLE;
+
+       ao_usb_alloc_buffers();
+
 #if USB_ECHO
        ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo");
 #endif
index 838f0dfcfbc948d73c2e9fa3e8c170ddca96abcb..a6a016622fa06aa364262d53db15b402cf9d92ea 100644 (file)
@@ -130,7 +130,7 @@ struct ao_adc {
 };
 
 #define AO_ADC_DUMP(p) \
-       printf("tick: %5u %5d batt: %5d\n", \
+       printf("tick: %5u batt %5d\n", \
               (p)->tick, \
               (p)->adc.v_batt);
 
@@ -168,7 +168,7 @@ struct ao_adc {
 #define ao_serial_btm_getchar  ao_serial2_getchar
 #define ao_serial_btm_putchar  ao_serial2_putchar
 #define _ao_serial_btm_pollchar        _ao_serial2_pollchar
-#define _ao_serial_btm_sleep   _ao_serial2_sleep
+#define _ao_serial_btm_sleep_for       _ao_serial2_sleep_for
 #define ao_serial_btm_set_speed ao_serial2_set_speed
 #define ao_serial_btm_drain    ao_serial2_drain
 #define ao_serial_btm_rx_fifo  (ao_stm_usart2.rx_fifo)
index 79f3896baff9a5adcb0f96785a0b43be0b812c46..cb2195efc928540c2f1455812cfa15a996a5a8f4 100644 (file)
@@ -280,9 +280,7 @@ ao_lco_igniter_status(void)
        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);
@@ -364,9 +362,7 @@ ao_lco_monitor(void)
                        delay = AO_MS_TO_TICKS(100);
                else
                        delay = AO_SEC_TO_TICKS(1);
-               ao_alarm(delay);
-               ao_sleep(&ao_lco_armed);
-               ao_clear_alarm();
+               ao_sleep_for(&ao_lco_armed, delay);
        }
 }
 
diff --git a/src/telelco-v0.2/ao_lco.c b/src/telelco-v0.2/ao_lco.c
deleted file mode 100644 (file)
index 4b5f7a9..0000000
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * 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_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 uint8_t ao_lco_got_channels;
-static uint16_t        ao_lco_tick_offset;
-
-static struct ao_pad_query     ao_pad_query;
-
-static void
-ao_lco_set_pad(uint8_t pad)
-{
-       ao_seven_segment_set(AO_LCO_PAD_DIGIT, pad + 1);
-}
-
-static void
-ao_lco_set_box(uint8_t box)
-{
-       ao_seven_segment_set(AO_LCO_BOX_DIGIT_1, box % 10);
-       ao_seven_segment_set(AO_LCO_BOX_DIGIT_10, 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 uint8_t
-ao_lco_pad_present(uint8_t pad)
-{
-       if (!ao_lco_got_channels || !ao_pad_query.channels)
-               return pad == 0;
-       if (pad >= AO_PAD_MAX_CHANNELS)
-               return 0;
-       return (ao_pad_query.channels >> pad) & 1;
-}
-
-static uint8_t
-ao_lco_pad_first(void)
-{
-       uint8_t pad;
-
-       for (pad = 0; pad < AO_PAD_MAX_CHANNELS; pad++)
-               if (ao_lco_pad_present(pad))
-                       return pad;
-       return 0;
-}
-
-static void
-ao_lco_input(void)
-{
-       static struct ao_event  event;
-       int8_t  dir, new_box, new_pad;
-
-       ao_beep_for(AO_BEEP_MID, AO_MS_TO_TICKS(200));
-       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) {
-                                       dir = (int8_t) event.value;
-                                       new_pad = ao_lco_pad;
-                                       do {
-                                               new_pad += dir;
-                                               if (new_pad > AO_PAD_MAX_CHANNELS)
-                                                       new_pad = 0;
-                                               else if (new_pad < 0)
-                                                       new_pad = AO_PAD_MAX_CHANNELS - 1;
-                                               if (new_pad == ao_lco_pad)
-                                                       break;
-                                       } while (!ao_lco_pad_present(new_pad));
-                                       if (new_pad != ao_lco_pad) {
-                                               ao_lco_pad = new_pad;
-                                               ao_lco_set_pad(ao_lco_pad);
-                                       }
-                               }
-                               break;
-                       case AO_QUADRATURE_BOX:
-                               if (!ao_lco_armed) {
-                                       dir = (int8_t) event.value;
-                                       new_box = ao_lco_box;
-                                       do {
-                                               new_box += dir;
-                                               if (new_box > ao_lco_max_box)
-                                                       new_box = ao_lco_min_box;
-                                               else if (new_box < ao_lco_min_box)
-                                                       new_box = ao_lco_max_box;
-                                               if (new_box == ao_lco_box)
-                                                       break;
-                                       } while (!ao_lco_box_present(new_box));
-                                       if (ao_lco_box != new_box) {
-                                               ao_lco_box = new_box;
-                                               ao_lco_got_channels = 0;
-                                               ao_lco_set_box(ao_lco_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 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) {
-               c = ao_lco_got_channels;
-               ao_lco_got_channels = 1;
-               ao_lco_valid = 1;
-               if (!c) {
-                       ao_lco_pad = ao_lco_pad_first();
-                       ao_lco_set_pad(ao_lco_pad);
-               }
-       } else
-               ao_lco_valid = 0;
-
-#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_reset_present(void)
-{
-       ao_lco_min_box = 0xff;
-       ao_lco_max_box = 0x00;
-       memset(ao_lco_box_mask, 0, sizeof (ao_lco_box_mask));
-}
-
-static void
-ao_lco_box_set_present(uint8_t box)
-{
-       if (box < ao_lco_min_box)
-               ao_lco_min_box = box;
-       if (box > ao_lco_max_box)
-               ao_lco_max_box = 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;
-       int8_t          try;
-       uint8_t         box;
-
-       ao_lco_box_reset_present();
-       for (box = 0; box < AO_PAD_MAX_BOXES; box++) {
-               if ((box % 10) == 0)
-                       ao_lco_set_box(box);
-               for (try = 0; try < 3; try++) {
-                       tick_offset = 0;
-                       r = ao_lco_query(box, &ao_pad_query, &tick_offset);
-                       PRINTD("box %d result %d\n", box, r);
-                       if (r == AO_RADIO_CMAC_OK) {
-                               ao_lco_box_set_present(box);
-                               ao_delay(AO_MS_TO_TICKS(30));
-                               break;
-                       }
-               }
-       }
-       if (ao_lco_min_box <= ao_lco_max_box)
-               ao_lco_box = ao_lco_min_box;
-       else
-               ao_lco_min_box = ao_lco_max_box = ao_lco_box = 0;
-       ao_lco_valid = 0;
-       ao_lco_got_channels = 0;
-       ao_lco_pad = 0;
-       ao_lco_set_pad(ao_lco_pad);
-       ao_lco_set_box(ao_lco_box);
-}
-
-static void
-ao_lco_igniter_status(void)
-{
-       uint8_t         c;
-
-       for (;;) {
-               ao_sleep(&ao_pad_query);
-               if (!ao_lco_valid) {
-                       ao_led_on(AO_LED_RED);
-                       ao_led_off(AO_LED_GREEN|AO_LED_AMBER);
-                       continue;
-               }
-               PRINTD("RSSI %d\n", ao_radio_cmac_rssi);
-               if (ao_radio_cmac_rssi < -90) {
-                       ao_led_on(AO_LED_AMBER);
-                       ao_led_off(AO_LED_RED|AO_LED_GREEN);
-               } else {
-                       ao_led_on(AO_LED_GREEN);
-                       ao_led_off(AO_LED_RED|AO_LED_AMBER);
-               }
-               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" },
-       { ao_lco_search,        "s\0Search for pad boxes" },
-       { 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.2/ao_lco.h b/src/telelco-v0.2/ao_lco.h
deleted file mode 100644 (file)
index 253f970..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * 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.3/.gitignore b/src/telelco-v0.3/.gitignore
new file mode 100644 (file)
index 0000000..a32ec26
--- /dev/null
@@ -0,0 +1,2 @@
+ao_product.h
+telelco*.elf
diff --git a/src/telelco-v0.3/Makefile b/src/telelco-v0.3/Makefile
new file mode 100644 (file)
index 0000000..83d3fc4
--- /dev/null
@@ -0,0 +1,108 @@
+#
+# AltOS build for TeleLCO
+#
+#
+
+include ../stm/Makefile.defs
+
+INC = \
+       ao.h \
+       ao_arch.h \
+       ao_arch_funcs.h \
+       ao_boot.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 \
+       ao_cc1200_CC1200.h \
+       ao_cc1200.h \
+       ao_debounce.h \
+       stm32l.h
+
+#
+# Common AltOS sources
+#
+
+#PROFILE=ao_profile.c
+#PROFILE_DEF=-DAO_PROFILE=1
+
+ALTOS_SRC = \
+       ao_boot_chain.c \
+       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_eeprom_stm.c \
+       ao_fast_timer.c \
+       ao_lcd_stm.c \
+       ao_usb_stm.c \
+       ao_exti_stm.c \
+       ao_cc1200.c \
+       ao_radio_cmac.c \
+       ao_aes.c \
+       ao_aes_tables.c \
+       ao_fec_tx.c \
+       ao_fec_rx.c \
+       ao_seven_segment.c \
+       ao_debounce.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.3
+PRODUCT_DEF=-DTELELCO
+IDPRODUCT=0x0023
+
+CFLAGS = $(PRODUCT_DEF) $(STM_CFLAGS) $(PROFILE_DEF) -Os -g
+
+PROGNAME=telelco-v0.3
+PROG=$(PROGNAME)-$(VERSION).elf
+HEX=$(PROGNAME)-$(VERSION).ihx
+
+SRC=$(ALTOS_SRC) ao_telelco.c
+OBJ=$(SRC:.c=.o)
+
+all: $(PROG) $(HEX)
+
+$(PROG): Makefile $(OBJ) altos.ld
+       $(call quiet,CC) $(LDFLAGS) $(CFLAGS) -o $(PROG) $(OBJ) $(LIBS)
+
+../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 $(PROGNAME)-*.ihx
+       rm -f ao_product.h
+
+install:
+
+uninstall:
diff --git a/src/telelco-v0.3/ao_pins.h b/src/telelco-v0.3/ao_pins.h
new file mode 100644 (file)
index 0000000..92095a7
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * 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)
+
+#define AO_CC1200_FOSC         40000000
+
+/* 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 USE_EEPROM_CONFIG      1
+#define USE_STORAGE_CONFIG     0
+#define HAS_USB                        1
+#define HAS_BEEP               1
+#define HAS_RADIO              1
+#define HAS_RADIO_RATE         1
+#define HAS_TELEMETRY          0
+#define HAS_AES                        1
+
+#define HAS_SPI_1              0
+#define SPI_1_PA5_PA6_PA7      0
+#define SPI_1_PB3_PB4_PB5      0
+#define SPI_1_PE13_PE14_PE15   0
+
+#define HAS_SPI_2              1       /* CC1120 */
+#define SPI_2_PB13_PB14_PB15   0
+#define SPI_2_PD1_PD3_PD4      1
+#define SPI_2_GPIO             (&stm_gpiod)
+#define SPI_2_SCK              1
+#define SPI_2_MISO             3
+#define SPI_2_MOSI             4
+#define SPI_2_OSPEEDR          STM_OSPEEDR_10MHz
+
+#define HAS_I2C_1              0
+
+#define HAS_I2C_2              0
+
+#define PACKET_HAS_SLAVE       0
+#define PACKET_HAS_MASTER      0
+
+#define FAST_TIMER_FREQ                10000   /* .1ms for debouncing */
+
+/*
+ * Radio is a cc1200 connected via SPI
+ */
+
+#define AO_RADIO_CAL_DEFAULT   5695733
+
+#define AO_FEC_DEBUG           0
+#define AO_CC1200_SPI_CS_PORT  (&stm_gpiod)
+#define AO_CC1200_SPI_CS_PIN   0
+#define AO_CC1200_SPI_BUS      AO_SPI_2_PD1_PD3_PD4
+#define AO_CC1200_SPI          stm_spi2
+
+#define AO_CC1200_INT_PORT             (&stm_gpioc)
+#define AO_CC1200_INT_PIN              (15)
+
+#define AO_CC1200_INT_GPIO     2
+#define AO_CC1200_INT_GPIO_IOCFG       CC1200_IOCFG2
+
+#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_AMBER          8
+#define LED_PIN_GREEN          9
+#define LED_PIN_CONTINUITY_3   10
+#define LED_PIN_CONTINUITY_2   11
+#define LED_PIN_CONTINUITY_1   12
+#define LED_PIN_CONTINUITY_0   13
+#define LED_PIN_REMOTE_ARM     14
+#define AO_LED_RED             (1 << LED_PIN_RED)
+#define AO_LED_AMBER           (1 << LED_PIN_AMBER)
+#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_AMBER |         \
+                                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 LCD_DEBUG              0
+#define SEVEN_SEGMENT_DEBUG    0
+
+#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 */            \
+               (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 */           \
+               (1 << 22) | /* PC4 */           \
+               (1 << 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 */            \
+               (0 << 1) | /* PA9 */            \
+               (0 << 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_STATIC
+
+#define AO_LCD_PER_DIGIT       1
+
+#define AO_LCD_DIGITS          3
+#define AO_LCD_SEGMENTS                8
+
+#define AO_SEGMENT_MAP {                       \
+               /* pad segments */              \
+               { 0, 14 },                      \
+               { 0, 13 },                      \
+               { 0, 15 },                      \
+               { 0, 17 },                      \
+               { 0, 16 },                      \
+               { 0, 8 },                       \
+               { 0, 9 },                       \
+               { 0, 7 },                       \
+               /* box1 segments */             \
+               { 0, 10 },                      \
+               { 0, 6 },                       \
+               { 0, 11 },                      \
+               { 0, 12 },                      \
+               { 0, 21 },                      \
+               { 0, 19 },                      \
+               { 0, 20 },                      \
+               { 0, 18 },                      \
+               /* box0 segments */             \
+               { 0, 22 },                      \
+               { 0, 4 },                       \
+               { 0, 23 },                      \
+               { 0, 5 },                       \
+               { 0, 3 },                       \
+               { 0, 1 },                       \
+               { 0, 2 },                       \
+               { 0, 0 },                       \
+}
+
+/*
+ * Use event queue for input devices
+ */
+
+#define AO_EVENT               1
+
+/*
+ * Knobs
+ */
+
+#define AO_QUADRATURE_COUNT    2
+
+#define AO_QUADRATURE_0_PORT   &stm_gpioe
+#define AO_QUADRATURE_0_A      3
+#define AO_QUADRATURE_0_B      2
+
+#define AO_QUADRATURE_PAD      0
+
+#define AO_QUADRATURE_1_PORT   &stm_gpioe
+#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_gpioe
+#define AO_BUTTON_0            4
+
+#define AO_BUTTON_ARM          0
+
+#define AO_BUTTON_1_PORT       &stm_gpioe
+#define AO_BUTTON_1            5
+
+#define AO_BUTTON_FIRE         1
+
+#endif /* _AO_PINS_H_ */
diff --git a/src/telelco-v0.3/ao_telelco.c b/src/telelco-v0.3/ao_telelco.c
new file mode 100644 (file)
index 0000000..d9f7c69
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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>
+#include <ao_eeprom.h>
+
+int
+main(void)
+{
+       ao_clock_init();
+       
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_GREEN);
+       ao_task_init();
+
+       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_eeprom_init();
+       
+       ao_radio_init();
+
+       ao_usb_init();
+
+       ao_config_init();
+       
+       ao_lco_init();
+       ao_lco_cmd_init();
+//     ao_radio_cmac_cmd_init();
+       
+       ao_start_scheduler();
+       return 0;
+}
diff --git a/src/telelco-v0.3/flash-loader/Makefile b/src/telelco-v0.3/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..679e61b
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telelco-v0.2
+include $(TOPDIR)/stm/Makefile-flash.defs
diff --git a/src/telelco-v0.3/flash-loader/ao_pins.h b/src/telelco-v0.3/flash-loader/ao_pins.h
new file mode 100644 (file)
index 0000000..6c8ff7e
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public 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_
+
+/* External crystal at 8MHz */
+#define AO_HSE         8000000
+
+#include <ao_flash_stm_pins.h>
+
+/* Arm switch. Press at power on to get boot loader */
+
+#define AO_BOOT_PIN            1
+#define AO_BOOT_APPLICATION_GPIO       stm_gpioe
+#define AO_BOOT_APPLICATION_PIN                4
+#define AO_BOOT_APPLICATION_VALUE      1
+#define AO_BOOT_APPLICATION_MODE       AO_EXTI_MODE_PULL_UP
+
+#endif /* _AO_PINS_H_ */
index fbbc4bd97004997850c1c78939dd48b6d35abe7e..f71c3052d3e175d881af95f7daa3fa929456fd4f 100644 (file)
@@ -554,9 +554,9 @@ ao_insert(void)
 
                        ao_quaternion_rotate(&ao_out, &ao_x, &ao_rotation);
 
+#if 0
                        int     out = floor (atan2(ao_out.y, ao_out.x) * 180 / M_PI);
 
-#if 0
                        printf ("%7.2f state %-8.8s height %8.4f tilt %4d rot %4d mag_tilt %4d mag_rot %4d\n",
                                time,
                                ao_state_names[ao_flight_state],
@@ -717,7 +717,7 @@ ao_sleep(void *wchan)
                                        break;
                        }
 #if TELEMEGA
-                       if (log_format == AO_LOG_FORMAT_TELEMEGA && nword == 30 && strlen(words[0]) == 1) {
+                       if ((log_format == AO_LOG_FORMAT_TELEMEGA_OLD || log_format == AO_LOG_FORMAT_TELEMEGA) && nword == 30 && strlen(words[0]) == 1) {
                                int     i;
                                struct ao_ms5607_value  value;
 
@@ -895,7 +895,6 @@ ao_sleep(void *wchan)
                                ao_config.accel_zero_along = atoi(words[3]);
                                ao_config.accel_zero_across = atoi(words[5]);
                                ao_config.accel_zero_through = atoi(words[7]);
-                               printf ("%d %d %d\n", ao_config.accel_zero_along, ao_config.accel_zero_across, ao_config.accel_zero_through);
 #endif
                        } else if (nword >= 4 && strcmp(words[0], "Main") == 0) {
                                ao_config.main_deploy = atoi(words[2]);
index abbdbbcc1207cb30c56755e2fe6da99c82e78027..49798f1c85aa8ae6749e680d75151ff7aa44b897 100644 (file)
@@ -12,6 +12,7 @@ INC = \
        ao_pins.h \
        ao_product.h \
        ao_task.h \
+       ao_adc_fast.h \
        stm32f0.h
 
 #
@@ -31,6 +32,7 @@ ALTOS_SRC = \
        ao_boot_chain.c \
        ao_cmd.c \
        ao_usb_stm.c \
+       ao_trng.c \
        ao_task.c \
        ao_product.c
 
index 2375944438c88f635afddc48e515f3479626f64b..1997d2052a49d37ccde48087416c7c67a4fc2c75 100644 (file)
@@ -60,4 +60,8 @@
 #define AO_CRC_WIDTH   32
 #define AO_CRC_INIT    0xffffffff
 
+/* TRNG */
+#define AO_LED_TRNG_READ       AO_LED_RED
+#define AO_LED_TRNG_WRITE      AO_LED_GREEN
+
 #endif /* _AO_PINS_H_ */
index e1f43cdd17fd2a4062717ec70220aaa3f773f960..42713b6ec842fb2023e9e4441527cdbdf6e5ed08 100644 (file)
 #include <ao.h>
 #include <ao_adc_fast.h>
 #include <ao_crc.h>
-
-static void
-ao_trng_fetch(void)
-{
-       static uint16_t *buffer[2];
-       uint32_t        kbytes = 1;
-       uint32_t        count;
-       int             usb_buf_id;
-       int             i;
-       uint16_t        *buf;
-       uint32_t        *rnd;
-
-       if (!buffer[0]) {
-               buffer[0] = ao_usb_alloc();
-               buffer[1] = ao_usb_alloc();
-               if (!buffer[0])
-                       return;
-       }
-
-       ao_cmd_decimal();
-       if (ao_cmd_status == ao_cmd_success)
-               kbytes = ao_cmd_lex_u32;
-       else
-               ao_cmd_status = ao_cmd_success;
-       usb_buf_id = 0;
-       count = kbytes * (1024/AO_USB_IN_SIZE);
-
-       ao_crc_reset();
-
-       ao_led_on(AO_LED_GREEN);
-       while (count--) {
-               buf = buffer[usb_buf_id];
-//             printf ("before get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
-               rnd = (uint32_t *) ao_adc_get(AO_USB_IN_SIZE);  /* one 16-bit value per output byte */
-//             printf ("after get: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
-               for (i = 0; i < 32; i++)
-                       *buf++ = ao_crc_in_32_out_16(*rnd++);
-               ao_adc_ack(AO_USB_IN_SIZE);
-//             printf ("after ack: head %3d tail %3d running %d\n", ao_adc_ring_head, ao_adc_ring_tail, ao_adc_running); flush();
-               ao_led_toggle(AO_LED_GREEN|AO_LED_RED);
-               ao_usb_write(buffer[usb_buf_id], AO_USB_IN_SIZE);
-               ao_led_toggle(AO_LED_GREEN|AO_LED_RED);
-               usb_buf_id = 1-usb_buf_id;
-       }
-       ao_led_off(AO_LED_GREEN|AO_LED_RED);
-       flush();
-}
-
-static const struct ao_cmds usbtrng_cmds[] = {
-       { ao_trng_fetch,        "f <kbytes>\0Fetch a block of numbers" },
-       { 0, NULL },
-};
+#include <ao_trng.h>
 
 void main(void)
 {
@@ -86,7 +35,8 @@ void main(void)
 
        ao_usb_init();
 
-       ao_cmd_register(usbtrng_cmds);
+       ao_trng_init();
+
        ao_led_off(AO_LED_RED);
 
        ao_start_scheduler();
index 5f2eb8e895557a492405c016336cac51dd3eedea..3ab8d16ec474aba3403cc9a6654c24f4868eefa9 100644 (file)
@@ -1,54 +1,58 @@
-#!/bin/sh
+#!/usr/bin/nickle
 
 autoimport ParseArgs;
 
+file  out = stdout;
+
 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);
+       File::fprintf(out, "/* %s */\n", description);
+       File::fprintf(out, "#define AO_%s_LEN 0x%02x\n", description, len * 2 + 2);
+       File::fprintf(out, "#define AO_%s_STRING \"%s\"\n", description, a);
+       File::fprintf(out, "#define AO_%s_UCS2", description);
        for (int i = 0; i < len; i++) {
                int     c = a[i];
                if (i > 0)
-                       printf(",");
+                       File::fprintf(out, ",");
                if (0x20 <= c && c < 128)
-                       printf(" '%c', 0", c);
+                       File::fprintf(out, " '%c', 0", c);
                else
-                       printf(" LE_WORD(0x%04x),", c);
+                       File::fprintf(out, " LE_WORD(0x%04x),", c);
        }
-       printf("\n\n");
+       File::fprintf(out, "\n\n");
 }
 
 void
 write_string(string a, string description)
 {
-       printf ("/* %s */\n", description);
-       printf ("#define AO_%s_STRING \"%s\"\n", description, a);
+       File::fprintf(out, "/* %s */\n", description);
+       File::fprintf(out, "#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);
+       File::fprintf(out, "/* %s */\n", description);
+       File::fprintf(out, "#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);
+       File::fprintf(out, "/* %s */\n", description);
+       File::fprintf(out, "#define AO_%s_NUMBER 0x%04x\n\n", description, a);
 }
 
 string manufacturer = "altusmetrum.org";
 string product = "TeleMetrum";
 string version = "0.0";
+string output = "";
 int serial = 1;
 int user_argind = 0;
+int id_vendor = 0xfffe;
 int id_product = 0x000a;
 
 argdesc argd = {
@@ -65,6 +69,12 @@ argdesc argd = {
                        .name = "product",
                        .expr_name = "prod",
                        .desc = "Product name." },
+               {
+                       .var = { .arg_int = &id_vendor },
+                       .abbr = 'V',
+                       .name = "id_vendor",
+                       .expr_name = "id_v",
+                       .desc = "Vendor ID." },
                {
                        .var = { .arg_int = &id_product },
                        .abbr = 'i',
@@ -83,6 +93,12 @@ argdesc argd = {
                        .name = "version",
                        .expr_name = "string",
                        .desc = "Program version." },
+               {
+                       .var = { .arg_string = &output },
+                       .abbr = 'o',
+                       .name = "output",
+                       .expr_name = "out",
+                       .desc = "Output file." },
        },
        .prog_name = "usb descriptors",
 };
@@ -92,11 +108,14 @@ main()
 {
        string[dim(argv)-1] nargv = {[n] = argv[n+1]};
        parseargs(&argd, &nargv);
+       if (output != "")
+               out = File::open(output, "w");
        write_ucs2(manufacturer, "iManufacturer");
        write_ucs2(product, "iProduct");
        write_ucs2(sprintf("%06d", serial), "iSerial");
        write_int(serial, "iSerial");
        write_hex(id_product, "idProduct");
+       write_hex(id_vendor, "idVendor");
        write_string(version, "iVersion");
 }
 
index fe3351762a926b8086e72d0b17ad619a6cafa057..0208e81d1000b17155f9b51ebbfa61f1518c979c 100644 (file)
@@ -23,8 +23,9 @@ import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import java.text.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPS
        extends AltosUIFrame
@@ -70,7 +71,7 @@ public class TeleGPS
 
        JTabbedPane             pane;
 
-       AltosUIMap              map;
+       AltosUIMapNew           map;
        TeleGPSInfo             gps_info;
        TeleGPSState            gps_state;
        AltosInfoTable          info_table;
@@ -172,7 +173,7 @@ public class TeleGPS
        }
 
        void load_maps() {
-               new AltosUIMapPreload(this);
+               new AltosUIMapPreloadNew(this);
        }
 
        void disconnect() {
@@ -372,7 +373,6 @@ public class TeleGPS
                                public void actionPerformed(ActionEvent e) {
                                        int rate = rates.rate();
                                        try {
-                                               System.out.printf("set rate %d\n", rate);
                                                reader.set_telemetry_rate(rate);
                                        } catch (TimeoutException te) {
                                        } catch (InterruptedException ie) {
@@ -480,8 +480,6 @@ public class TeleGPS
                bag = getContentPane();
                bag.setLayout(new GridBagLayout());
 
-               GridBagConstraints c = new GridBagConstraints();
-
                setTitle("TeleGPS");
 
                menu_bar = new JMenuBar();
@@ -491,25 +489,16 @@ public class TeleGPS
                monitor_menu = make_menu("Monitor", monitor_menu_entries);
                device_menu = make_menu("Device", device_menu_entries);
 
+               set_inset(3);
                frequencies = new AltosUIFreqList();
                frequencies.setEnabled(false);
-               c.gridx = 0;
-               c.gridy = 0;
-               c.fill = GridBagConstraints.NONE;
-               c.anchor = GridBagConstraints.WEST;
-               c.weightx = 0;
-               c.gridwidth = 1;
-               bag.add(frequencies, c);
+               bag.add(frequencies, constraints (0, 1));
 
                rates = new AltosUIRateList();
                rates.setEnabled(false);
-               c.gridx = 1;
-               c.gridy = 0;
-               c.fill = GridBagConstraints.NONE;
-               c.anchor = GridBagConstraints.WEST;
-               c.weightx = 0;
-               c.gridwidth = 1;
-               bag.add(rates, c);
+               bag.add(rates, constraints(1, 1));
+               next_row();
+               set_inset(0);
 
                displays = new LinkedList<AltosFlightDisplay>();
 
@@ -517,13 +506,9 @@ public class TeleGPS
 
                /* TeleGPS status is always visible */
                telegps_status = new TeleGPSStatus();
-               c.gridx = 0;
-               c.gridy = 1;
-               c.fill = GridBagConstraints.HORIZONTAL;
-               c.weightx = 1;
-               c.gridwidth = 2;
-               bag.add(telegps_status, c);
-               c.gridwidth = 1;
+               bag.add(telegps_status, constraints(0, 3, GridBagConstraints.HORIZONTAL));
+               next_row();
+
                displays.add(telegps_status);
 
 
@@ -533,15 +518,9 @@ public class TeleGPS
                pane = new JTabbedPane();
 
                /* 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);
-
-               map = new AltosUIMap();
+               bag.add(pane, constraints(0, 3, GridBagConstraints.BOTH));
+
+               map = new AltosUIMapNew();
                pane.add(map.getName(), map);
                displays.add(map);
 
@@ -650,7 +629,6 @@ public class TeleGPS
        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");
@@ -675,16 +653,7 @@ public class TeleGPS
                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]);
-                                       AltosUIMap.prefetch_maps(lat, lon);
-                                       i += 2;
-                               }
-                       } else if (args[i].equals("--replay"))
+                       else if (args[i].equals("--replay"))
                                process = process_replay;
                        else if (args[i].equals("--kml"))
                                process = process_kml;
index 55763e22458493ad451b15857107678a0bc4ea54..e2cf12587e70203f146150bd4c47969704a7660e 100644 (file)
@@ -22,8 +22,8 @@ import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.text.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPSConfig implements ActionListener {
 
index 5e700b72c2bebaa68264458f97506bf8c3383fca..8d4916aa2e5c539240f2c47cee8c812d3d7be719 100644 (file)
 
 package org.altusmetrum.telegps;
 
+import java.text.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 import javax.swing.event.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPSConfigUI
        extends AltosUIDialog
@@ -40,6 +41,7 @@ public class TeleGPSConfigUI
        JLabel                  rate_label;
        JLabel                  aprs_interval_label;
        JLabel                  aprs_ssid_label;
+       JLabel                  aprs_format_label;
        JLabel                  flight_log_max_label;
        JLabel                  callsign_label;
        JLabel                  tracker_motion_label;
@@ -57,6 +59,7 @@ public class TeleGPSConfigUI
        AltosUIRateList         rate_value;
        JComboBox<String>       aprs_interval_value;
        JComboBox<Integer>      aprs_ssid_value;
+       JComboBox<String>       aprs_format_value;
        JComboBox<String>       flight_log_max_value;
        JTextField              callsign_value;
        JComboBox<String>       tracker_motion_value;
@@ -165,11 +168,20 @@ public class TeleGPSConfigUI
 
        void set_aprs_ssid_tool_tip() {
                if (aprs_ssid_value.isEnabled())
-                       aprs_interval_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
-               else if (aprs_interval_value.isEnabled())
-                       aprs_interval_value.setToolTipText("Software version doesn't support setting the APRS SSID");
+                       aprs_ssid_value.setToolTipText("Set the APRS SSID (secondary station identifier)");
+               else if (aprs_ssid_value.isEnabled())
+                       aprs_ssid_value.setToolTipText("Software version doesn't support setting the APRS SSID");
                else
-                       aprs_interval_value.setToolTipText("Hardware doesn't support APRS");
+                       aprs_ssid_value.setToolTipText("Hardware doesn't support APRS");
+       }
+
+       void set_aprs_format_tool_tip() {
+               if (aprs_format_value.isEnabled())
+                       aprs_format_value.setToolTipText("Set the APRS format (compressed/uncompressed)");
+               else if (aprs_format_value.isEnabled())
+                       aprs_format_value.setToolTipText("Software version doesn't support setting the APRS format");
+               else
+                       aprs_format_value.setToolTipText("Hardware doesn't support APRS");
        }
 
        void set_flight_log_max_tool_tip() {
@@ -413,6 +425,33 @@ public class TeleGPSConfigUI
                set_aprs_ssid_tool_tip();
                row++;
 
+               /* APRS format */
+               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;
+               aprs_format_label = new JLabel("APRS format:");
+               pane.add(aprs_format_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;
+               aprs_format_value = new JComboBox<String>(AltosLib.ao_aprs_format_name);
+               aprs_format_value.setEditable(false);
+               aprs_format_value.addItemListener(this);
+               aprs_format_value.setMaximumRowCount(AltosLib.ao_aprs_format_name.length);
+               pane.add(aprs_format_value, c);
+               set_aprs_format_tool_tip();
+               row++;
+
                /* Callsign */
                c = new GridBagConstraints();
                c.gridx = 0; c.gridy = row;
@@ -648,7 +687,11 @@ public class TeleGPSConfigUI
                        String motion = tracker_motion_value.getSelectedItem().toString();
                        tracker_motion_label.setText(get_tracker_motion_label());
                        set_tracker_motion_values();
-                       set_tracker_motion((int) (AltosConvert.height.parse(motion, !imperial_units) + 0.5));
+                       try {
+                               int m = (int) (AltosConvert.height.parse_locale(motion, !imperial_units) + 0.5);
+                               set_tracker_motion(m);
+                       } catch (ParseException pe) {
+                       }
                }
                if (!was_dirty)
                        set_clean();
@@ -848,7 +891,12 @@ public class TeleGPSConfigUI
        }
 
        public int tracker_motion() throws AltosConfigDataException {
-               return (int) AltosConvert.height.parse(tracker_motion_value.getSelectedItem().toString());
+               String str = tracker_motion_value.getSelectedItem().toString();
+               try {
+                       return (int) (AltosConvert.height.parse_locale(str) + 0.5);
+               } catch (ParseException pe) {
+                       throw new AltosConfigDataException("invalid tracker motion %s", str);
+               }
        }
 
        public void set_tracker_interval(int tracker_interval) {
@@ -894,4 +942,16 @@ public class TeleGPSConfigUI
                Integer i = (Integer) aprs_ssid_value.getSelectedItem();
                return i;
        }
+
+       public void set_aprs_format(int new_aprs_format) {
+               aprs_format_value.setVisible(new_aprs_format >= 0);
+               aprs_format_label.setVisible(new_aprs_format >= 0);
+
+               aprs_format_value.setSelectedIndex(Math.max(0,new_aprs_format));
+               set_aprs_format_tool_tip();
+       }
+
+       public int aprs_format() throws AltosConfigDataException {
+               return aprs_format_value.getSelectedIndex();
+       }
 }
index 09610f5908a5dda08123e481beef37e552ca3cef..fa394b17fe3c0d2b1e830e861cbeda9e1baf4b39 100644 (file)
@@ -21,8 +21,8 @@ import java.awt.*;
 import javax.swing.*;
 import java.io.*;
 import java.text.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPSDisplayThread extends Thread {
 
index d3adc748aa5055b9cb772b68f44e7c0ffbe1b5c2..8fe479cc235784f69b48bcd7bf67327c266d1d3f 100644 (file)
@@ -26,8 +26,8 @@ import javax.swing.*;
 import java.io.*;
 import java.util.concurrent.*;
 import java.util.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 import org.jfree.chart.ChartPanel;
 import org.jfree.chart.JFreeChart;
@@ -38,7 +38,7 @@ public class TeleGPSGraphUI extends AltosUIFrame
        JTabbedPane             pane;
        AltosGraph              graph;
        AltosUIEnable           enable;
-       AltosUIMap              map;
+       AltosUIMapNew           map;
        AltosState              state;
        AltosFlightStats        stats;
        AltosGraphDataSet       graphDataSet;
@@ -69,7 +69,7 @@ public class TeleGPSGraphUI extends AltosUIFrame
                graph = new AltosGraph(enable, stats, graphDataSet);
                statsTable = new AltosFlightStatsTable(stats);
 
-               map = new AltosUIMap();
+               map = new AltosUIMapNew();
 
                pane.add("Graph", graph.panel);
                pane.add("Configure Graph", enable);
index f20a88300bc6ae04b13071a21e8d68182c04db45..6baccce93fe7ec1d7e3b6d00a81827cffd5b6694 100644 (file)
@@ -21,8 +21,8 @@ import java.util.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPSInfo extends AltosUIFlightTab {
 
index 6d7b8985569e189bca8e76303ed7825c4a9ac639..d0b843e14a59ab5d41b97272b0100c48a84f5b2f 100644 (file)
@@ -22,7 +22,7 @@ import java.awt.event.*;
 import java.beans.*;
 import javax.swing.*;
 import javax.swing.event.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPSPreferences
        extends AltosUIConfigure
index 18f500b907d56ef726d65cbfd4b52d6cdcdabfe3..dac7da5213230785ed52febe596c59777b3f8eda 100644 (file)
@@ -21,8 +21,8 @@ import java.util.*;
 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPSState extends AltosUIFlightTab {
 
@@ -161,6 +161,26 @@ public class TeleGPSState extends AltosUIFlightTab {
                }
        }
 
+       class ReceiverBattery extends AltosUIVoltageIndicator {
+
+               public double voltage(AltosState state) { return AltosLib.MISSING; }
+
+               public double good() { return AltosLib.ao_battery_good; }
+
+               public boolean hide(AltosState state, AltosListenerState listener_state, int i) {
+                       return value(state, listener_state, i) == AltosLib.MISSING;
+               }
+
+               public double value(AltosState state, AltosListenerState listener_state, int i) {
+                       if (listener_state == null)
+                               return AltosLib.MISSING;
+                       return listener_state.battery;
+               }
+
+               public ReceiverBattery (AltosUIFlightTab container, int y) {
+                       super(container, y, "Receiver Battery", 2);
+               }
+       }
 
        public void labels(Container container, int y) {
                GridBagLayout           layout = (GridBagLayout)(container.getLayout());
@@ -203,5 +223,6 @@ public class TeleGPSState extends AltosUIFlightTab {
                add(new FirmwareVersion(this, y++));
                add(new FlightLogMax(this, y++));
                add(new BatteryVoltage(this, y++));
+               add(new ReceiverBattery(this, y++));
        }
 }
index 050a844969821899abcf6e7477ee732aa9f09de4..896df9d49c78cb4eec39641b6988b1949384b6f2 100644 (file)
@@ -19,8 +19,8 @@ package org.altusmetrum.telegps;
 
 import java.awt.*;
 import javax.swing.*;
-import org.altusmetrum.altoslib_6.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altoslib_8.*;
+import org.altusmetrum.altosuilib_8.*;
 
 public class TeleGPSStatus extends JComponent implements AltosFlightDisplay {
        GridBagLayout   layout;
index 9c085a5b000ea39f78f9e6c08b306b162f813224..eef0a034087f42dcd9c50f239b54e96db4967d7f 100644 (file)
@@ -18,7 +18,7 @@
 package org.altusmetrum.telegps;
 
 import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
 
 public class TeleGPSStatusUpdate implements ActionListener {
 
index 446567157c1f25877d9720aec561c8a974c9675f..b0b5d6a65b0023e746e3f6f2ae01a42fccf24523 100644 (file)
@@ -103,16 +103,17 @@ Section "${REG_NAME} Application"
        File "freetts.jar"
        File "jfreechart.jar"
        File "jcommon.jar"
+       File "../icon/${WIN_APP_EXE}"
 
        File "*.dll"
 
        File "../icon/${WIN_APP_ICON}"
 
-       CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}" "" "$INSTDIR\${WIN_APP_ICON}"
+       CreateShortCut "$SMPROGRAMS\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}" "" "$INSTDIR\${WIN_APP_ICON}"
 SectionEnd
 
 Section "${REG_NAME} Desktop Shortcut"
-       CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${FAT_NAME}"  "" "$INSTDIR\${WIN_APP_ICON}"
+       CreateShortCut "$DESKTOP\${REG_NAME}.lnk" "$INSTDIR\${WIN_APP_EXE}"  "" "$INSTDIR\${WIN_APP_ICON}"
 SectionEnd
 
 Section "TeleGPS, TeleDongle and TeleBT Firmware"
@@ -141,7 +142,6 @@ Section "File Associations"
 
        SetOutPath $INSTDIR
 
-       File "../icon/${WIN_APP_EXE}"
        File "../icon/${WIN_TELEM_EXE}"
        File "../icon/${WIN_EEPROM_EXE}"
 
@@ -153,15 +153,13 @@ Section "File Associations"
        DeleteRegKey   HKCR ".telem\${PROG_ID_EEPROM}"
        DeleteRegValue HKCR ".telem\OpenWithProgids" "${PROG_ID_EEPROM}"
 
-       SearchPath $1 "javaw.exe"
-
        ; .eeprom elements
 
        WriteRegStr HKCR "${PROG_ID_EEPROM}"            ""                              "Altus Metrum Log File"
        WriteRegStr HKCR "${PROG_ID_EEPROM}"            "FriendlyTypeName"              "Altus Metrum Log File"
        WriteRegStr HKCR "${PROG_ID_EEPROM}\CurVer"     ""                              "${PROG_ID_EEPROM}"
        WriteRegStr HKCR "${PROG_ID_EEPROM}\DefaultIcon" ""                             '"$INSTDIR\${WIN_EEPROM_EXE}",-101'
-  WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" ""                     '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+  WriteRegExpandStr HKCR "${PROG_ID_EEPROM}\shell\open\command" ""                     '"$INSTDIR\${WIN_APP_EXE}" "%1"'
 
        WriteRegStr HKCR ".eeprom"                      ""                              "${PROG_ID_EEPROM}"
        WriteRegStr HKCR ".eeprom"                      "PerceivedType"                 "Altus Metrum Log File"
@@ -176,7 +174,7 @@ Section "File Associations"
        WriteRegStr HKCR "${PROG_ID_TELEM}"             "FriendlyTypeName"              "Altus Metrum Telemetry File"
        WriteRegStr HKCR "${PROG_ID_TELEM}\CurVer"      ""                              "${PROG_ID_TELEM}"
        WriteRegStr HKCR "${PROG_ID_TELEM}\DefaultIcon" ""                              '"$INSTDIR\${WIN_TELEM_EXE}",-101'
-  WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" ""                      '"$1" -Djava.library.path="$INSTDIR" -jar "$INSTDIR\${FAT_NAME}" "%1"'
+  WriteRegExpandStr HKCR "${PROG_ID_TELEM}\shell\open\command" ""                      '"$INSTDIR\${WIN_APP_EXE}" "%1"'
 
        WriteRegStr HKCR ".telem"                       ""                              "${PROG_ID_TELEM}"
        WriteRegStr HKCR ".telem"                       "PerceivedType"                 "Altus Metrum Telemetry File"