+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
+++ /dev/null
-<?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>
--- /dev/null
+<?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>
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
$(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)
-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
*) 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
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.
# project structure.
# Project target.
-target=android-10
+target=android-12
# project structure.
# Project target.
-target=android-10
+target=android-12
android.library.reference.1=google-play-services_lib/
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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
+++ /dev/null
-<?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
+++ /dev/null
-<?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>
--- /dev/null
+<?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>
+++ /dev/null
-<?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
--- /dev/null
+<?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>
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>
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>
--- /dev/null
+<?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>
<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <style name="CustomTheme" parent="android:Theme.Holo">
+ </style>
+</resources>
<!-- 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>
<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>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+
+<resources>
+ <usb-device vendor-id="65534" />
+ <usb-device vendor-id="1027" />
+</resources>
//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; }
--- /dev/null
+/*
+ * 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");
+ }
+ }
+}
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;
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
// 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;
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;
// 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;
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;
}
} 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) {
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;
}
}
}
}
+ 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) {
}
}
- 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;
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;
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;
}
}
}
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 {
}
}
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;
}
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);
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);
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
//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
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) {
}
}
}
}
+ 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:
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);
+ }
}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
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)
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;
+ }
+ }
}
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";
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) {
}
public void remove(String key) {
+ AltosDebug.debug("remove preference %s\n", key);
editor.remove(key);
}
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;
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,
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;
- }
}
}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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() { }
+
+}
@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);
}
}
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;
+ }
+ }
}
--- /dev/null
+/*
+ * 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;
+ }
+}
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;
* 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;
* 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);
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);
// 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);
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);
- }
}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
- }
-
-}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+
+}
+++ /dev/null
-/*
- * 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());
- }
- }
-}
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() {
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);
}
}
-
}
--- /dev/null
+/*
+ * 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());
+ }
+ }
+}
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
@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;
}
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);
}
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;
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();
}
}
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();
}
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();
}
} 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");
}
}
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;
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;
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 {
@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);
}
}
+ /* 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);
// 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;
((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) {
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);
}
bin
classaltoslib.stamp
altoslib*.jar
+AltosVersion.java
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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;
* 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.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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.*;
/* HAS_APRS */
public int aprs_interval;
public int aprs_ssid;
+ public int aprs_format;
/* HAS_BEEP */
public int beep;
aprs_interval = -1;
aprs_ssid = -1;
+ aprs_format = -1;
beep = -1;
/* 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) {}
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)
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);
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)
* 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 {
* 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 */
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);
/*
* Sensor data conversion functions
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
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;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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 {
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 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.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
public interface AltosEepromMonitor {
* 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.*;
* 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.*;
* 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.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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);
--- /dev/null
+/*
+ * 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();
+}
* 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.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
--- /dev/null
+/*
+ * 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);
+}
* 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);
* 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.*;
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++]);
}
* 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;
* 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.*;
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;
* 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 {
* 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;
* 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;
* 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.*;
* 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.*;
* 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.*;
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);
* 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.*;
* 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);
* 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.*;
--- /dev/null
+/*
+ * 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();
+}
* 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;
end();
prev = null;
}
+ if (out != null) {
+ out.close();
+ out = null;
+ }
}
public void write(AltosState state) {
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 {
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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"; }
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
* 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.*;
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";
* 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;
* 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.*;
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;
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
}
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() {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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 {
* 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.*;
* 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"; }
* 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.*;
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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>();
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
+
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.util.concurrent.*;
* 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.*;
* 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) {
* 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 {
* 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 {
}
}
- 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);
}
}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
* 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;
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";
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;
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() {
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;
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;
+ }
+ }
}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.File;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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.*;
* 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 */
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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.*;
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());
}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
* 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;
* 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;
* 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;
* 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;
* 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;
* 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;
* 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;
* 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 {
* Track flight state from telemetry or eeprom data stream
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.io.*;
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 */
}
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);
}
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;
* 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.*;
* 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;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.text.*;
* 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 {
* 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.*;
* 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.*;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
import java.text.*;
* 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 {
* 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;
* 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;
* 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;
* 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 {
* 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 {
* 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 {
* 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) {
* 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.*;
* 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;
* 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 {
* 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;
* 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 {
* 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 {
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) {
public String say_units(double v) {
return say_units(v, AltosConvert.imperial_units);
}
-}
\ No newline at end of file
+}
* 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);
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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 {
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altoslib_6;
+package org.altusmetrum.altoslib_8;
public interface AltosWriter {
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
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 {
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;
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;
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 {
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
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);
}
}
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());
}
}
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);
}
}
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 {
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
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
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;
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;
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() {
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;
}
- 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() {
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)
}
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) {
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();
+ }
}
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
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 {
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;
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 = {
package altosui;
import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
public class AltosFlightStatusUpdate implements ActionListener {
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;
AltosDescent descent;
AltosLanded landed;
AltosCompanionInfo companion;
- AltosUIMap sitemap;
+ AltosUIMapNew sitemap;
boolean has_map;
boolean has_companion;
boolean has_state;
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");
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));
}
});
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)) {
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;
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
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);
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;
JTabbedPane pane;
AltosGraph graph;
AltosUIEnable enable;
- AltosUIMap map;
+ AltosUIMapNew map;
AltosState state;
AltosGraphDataSet graphDataSet;
AltosFlightStats stats;
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;
}
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;
AltosFlightStatus flightStatus;
AltosIgnitor ignitor;
AltosIdleMonitor thread;
+ AltosUIMapNew sitemap;
int serial;
boolean remote;
boolean has_ignitor;
+ boolean has_map;
void stop_display() {
if (thread != null) {
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);
// }
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);
}
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,
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());
/* 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() {
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
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);
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
LinkedBlockingQueue<String> command_queue;
- LinkedBlockingQueue<String> reply_queue;
-
class Igniter {
JRadioButton button;
JLabel status_label;
}
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;
set_ignite_status();
} else if (reply.equals("fired")) {
fired();
+ } else if (reply.startsWith("npyro")) {
+ npyro = Integer.parseInt(reply.substring(6));
+ make_ui();
}
}
}
}
- 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;
}
}
- int get_npyro() {
- send_command("get_npyro");
- String reply = get_reply();
- return Integer.parseInt(reply);
- }
-
boolean firing = false;
void start_fire(String which) {
void close() {
if (opened) {
send_command("quit");
- timer.stop();
}
+ if (timer != null)
+ timer.stop();
setVisible(false);
dispose();
}
private boolean open() {
command_queue = new LinkedBlockingQueue<String>();
- reply_queue = new LinkedBlockingQueue<String>();
opened = false;
device = AltosDeviceUIDialog.show(owner, Altos.product_any);
return false;
}
- public AltosIgniteUI(JFrame in_owner) {
-
- owner = in_owner;
-
- if (!open())
- return;
-
+ private void make_ui() {
group = new ButtonGroup();
Container pane = getContentPane();
timer_running = false;
timer.restart();
- owner = in_owner;
-
pane.setLayout(new GridBagLayout());
c.fill = GridBagConstraints.NONE;
y++;
- int npyro = get_npyro();
-
igniters = new Igniter[2 + npyro];
igniters[0] = new Igniter(this, "Apogee", AltosIgnite.Apogee, y++);
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");
+ }
+}
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 {
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 {
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;
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) {
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 {
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;
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++));
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();
}
void LoadMaps() {
- new AltosUIMapPreload(AltosUI.this);
+ new AltosUIMapPreloadNew(AltosUI.this);
}
void LaunchController() {
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;
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"
SetOutPath $INSTDIR
- File "../icon/${WIN_APP_EXE}"
File "../icon/${WIN_TELEM_EXE}"
File "../icon/${WIN_EEPROM_EXE}"
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"
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"
-AltosUIVersion.java
bin
classaltosuilib.stamp
altosuilib*.jar
* 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 {
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);
* 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;
* 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>();
* 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.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;
* 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
* 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;
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;
}
* 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;
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
import libaltosJNI.*;
* 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.*;
* 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.*;
* 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 {
* 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;
* 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 {
* 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.*;
* 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;
* 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;
* 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.filechooser.FileNameExtensionFilter;
import java.io.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
public class AltosFlashUI
extends AltosUIDialog
// Flash controller
AltosProgrammer programmer;
- private static String[] pair_programmed = {
+ private static final String[] pair_programmed_files = {
"teleballoon",
"telebt-v1",
"teledongle-v0",
"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;
}
+++ /dev/null
-/*
- * 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();
-}
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
import javax.swing.table.*;
* 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;
+++ /dev/null
-/*
- * 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);
-}
* 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.*;
* 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 {
* 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;
* 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;
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)
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
import javax.swing.*;
* 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.*;
* 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);
* 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
* 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.util.*;
import java.text.*;
import java.util.concurrent.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
class AltosScanResult {
String callsign;
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);
}
* 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.*;
/*
* 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;
* 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.*;
* 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> {
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++;
* 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;
* 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;
* 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();
* 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.*;
* 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.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.*;
* 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;
* 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.*;
}
}
}
+
+ 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);
* 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> {
* 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.*;
* 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.*;
--- /dev/null
+/*
+ * 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;
+ }
+}
* 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;
+++ /dev/null
-/*
- * 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;
- }
-}
* 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 {
* 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);
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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;
- }
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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;
- }
-}
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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>();
- }
-}
+++ /dev/null
-/*
- * 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);
- }
-}
--- /dev/null
+/*
+ * 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);
+ }
+}
+++ /dev/null
-/*
- * 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);
- }
-}
+++ /dev/null
-/*
- * 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;
- }
-
-}
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/*
- * 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());
- }
-}
+++ /dev/null
-/*
- * 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();
-}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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();
- }
-}
+++ /dev/null
-/*
- * 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);
-}
* 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.*;
* 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 {
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());
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(); }
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;
- }
- }
}
* 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 {
* 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> {
* 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.*;
* 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> {
* 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 {
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);
}
v[i] = value(state, listener_state, i);
else
v[i] = AltosLib.MISSING;
- if (hide(state, i))
+ if (hide(state, listener_state, i))
hide = true;
}
+++ /dev/null
-/*
- * 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;
- }
-}
* 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 {
* 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.*;
* 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;
* 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.*;
GrabNDrag.java \
AltosDevice.java \
AltosDeviceDialog.java \
- AltosFlightDisplay.java \
- AltosFontListener.java \
AltosPositionListener.java \
AltosUIConfigure.java \
AltosUIAxis.java \
AltosUIPreferencesBackend.java \
AltosUIPreferences.java \
AltosUISeries.java \
- AltosUIVersion.java \
AltosUSBDevice.java \
AltosVoice.java \
AltosDisplayThread.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 \
AltosUIFreqList.java \
AltosUITelemetryList.java \
AltosUIRateList.java \
+ AltosUIImage.java \
OSXAdapter.java
JAR=altosuilib_$(ALTOSUILIB_VERSION).jar
*/
-package org.altusmetrum.altosuilib_6;
+package org.altusmetrum.altosuilib_8;
import java.lang.reflect.*;
import java.util.HashMap;
;;
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
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")
echo""
echo "$PRODUCT-v$VERSION" serial "$serial" is ready to ship
+ echo "\007"
ret=0
;;
esac
#!/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
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*)
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
#!/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
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
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
;;
esac
-echo 'E 0' > $dev
+SERIAL=$SERIAL ./cal-freq $dev
./test-telegps
-SERIAL=$SERIAL ./cal-freq $dev
-
exit $?
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
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"
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
--- /dev/null
+ao-cal-freq
--- /dev/null
+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
--- /dev/null
+.\"
+.\" Copyright © 2009 Keith Packard <keithp@keithp.com>
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+.\" General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along
+.\" with this program; if not, write to the Free Software Foundation, Inc.,
+.\" 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+.\"
+.\"
+.TH AO-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
--- /dev/null
+/*
+ * 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);
+}
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);
}
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;
}
}
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;
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)
-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
--- /dev/null
+#!/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
--- /dev/null
+.\"
+.\" 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
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);
}
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) {
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);
.\" 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
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
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);
}
{
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;
}
}
- 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;
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);
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,
}
struct cc_usbdevs *
-cc_usbdevs_scan(void)
+cc_usbdevs_scan(int non_tty)
{
int e;
struct dirent **ents;
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 *));
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++) {
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);
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])
Makefile
src/Makedefs
altoslib/Makefile
+altoslib/AltosVersion.java
icon/Makefile
altosuilib/Makefile
-altosuilib/AltosUIVersion.java
altosui/Makefile
altosui/Info.plist
altosui/altos-windows.nsi
telegps/telegps-windows.nsi
altosdroid/Makefile
altosdroid/local.properties
+altosdroid/AndroidManifest.xml
ao-tools/Makefile
ao-tools/lib/Makefile
ao-tools/ao-rawload/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
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
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 \
micropeak-statistics.png \
MicroPeakUSB-2.0-inuse.jpg \
MicroPeakUSB-2.0.jpg \
+ monitor-idle.png \
scan-channels.png \
site-map.png \
table.png \
</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>
<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>
<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
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
</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
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>
</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'
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>
<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>
<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>
</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
--- /dev/null
+<?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>
</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>
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
--- /dev/null
+#!/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
-__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);
+}
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)
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
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;
package org.altusmetrum.micropeak;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
public class MicroDataPoint implements AltosUIDataPoint {
public double time;
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 {
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;
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 {
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 {
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;
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 = {
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.*;
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 {
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 {
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 {
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;
import java.util.*;
import java.io.*;
import libaltosJNI.*;
-import org.altusmetrum.altosuilib_6.*;
+import org.altusmetrum.altosuilib_8.*;
public interface MicroSerialLog {
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;
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;
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 {
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
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"
SetOutPath $INSTDIR
- File "../icon/${WIN_APP_EXE}"
File "../icon/${WIN_MPD_EXE}"
- SearchPath $1 "javaw.exe"
-
; application elements
DeleteRegKey HKCR "${PROG_ID}"
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
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
ao_button_init(void);
char
-ao_button_get(void) __critical;
+ao_button_get(uint16_t timeout) __critical;
void
ao_button_clear(void) __critical;
}
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;
/* 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
--- /dev/null
+ao_product.h
+chaoskey-*
--- /dev/null
+#
+# 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:
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+ao_product.h
+chaoskey*
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=chaoskey-v0.1
+include $(TOPDIR)/stmf0/Makefile-flash.defs
--- /dev/null
+/*
+ * 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_ */
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;
}
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);
#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
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);
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;
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();
}
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
*/
/* 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);
/* 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);
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();
}
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;
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)
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)
* 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))
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
#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)
(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] */
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();
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
--- /dev/null
+/*
+ * 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
+}
--- /dev/null
+/*
+ * 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_ */
}
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
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)
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;
}
}
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;
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
#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
#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;
}
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;
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",
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 */
};
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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_ */
#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;
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 {
#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 },
#endif
#define AO_CONFIG_MAJOR 1
-#define AO_CONFIG_MINOR 21
+#define AO_CONFIG_MINOR 22
#define AO_AES_LEN 16
#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
ao_packet_send(void);
uint8_t
-ao_packet_recv(void);
+ao_packet_recv(uint16_t timeout);
void
ao_packet_flush(void);
#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 [] =
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 */
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 */
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();
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;
/* 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;
}
ao_mutex_put(&ao_radio_cmac_mutex);
return i;
}
-
_ao_serial0_pollchar(void);
uint8_t
-_ao_serial0_sleep(void);
+_ao_serial0_sleep_for(uint16_t timeout);
void
ao_serial0_putchar(char c);
_ao_serial1_pollchar(void);
uint8_t
-_ao_serial1_sleep(void);
+_ao_serial1_sleep_for(uint16_t timeout);
void
ao_serial1_putchar(char c);
_ao_serial2_pollchar(void);
uint8_t
-_ao_serial2_sleep(void);
+_ao_serial2_sleep_for(uint16_t timeout);
void
ao_serial2_putchar(char c);
_ao_serial3_pollchar(void);
uint8_t
-_ao_serial3_sleep(void);
+_ao_serial3_sleep_for(uint16_t timeout);
void
ao_serial3_putchar(char c);
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;
void
ao_delay(uint16_t ticks)
{
- ao_alarm(ticks);
- ao_sleep(&ao_forever);
- ao_clear_alarm();
+ ao_sleep_for(&ao_forever, ticks);
}
void
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);
/* Clear any pending alarm */
void
ao_clear_alarm(void);
+#endif
/* Yield the processor to another task */
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);
}
}
}
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 */
};
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;
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"
ao_product.h
-microsplash-*
+microsplash-v*
+microsplash-load
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
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)
# Otherwise, print the full command line.
quiet ?= $($1)
-all: $(PROG) $(PROG).hex
+all: $(PROG) $(HEX) $(SCRIPT)
CHECK=sh ../util/check-avr-mem
$(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) > $@
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:
--- /dev/null
+#!/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"
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));
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;
}
(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();
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,
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);
}
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
}
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
}
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
}
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
#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) |
#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;
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) |
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);
}
/* 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();
}
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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_ */
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
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;
}
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
}
ao_usb_set_address(0);
+
+ ao_usb_running = 0;
}
static 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,
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,
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,
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;
{
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;
{
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);
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
};
#define AO_ADC_DUMP(p) \
- printf("tick: %5u %5d batt: %5d\n", \
+ printf("tick: %5u batt %5d\n", \
(p)->tick, \
(p)->adc.v_batt);
#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)
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);
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);
}
}
+++ /dev/null
-/*
- * 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
-}
+++ /dev/null
-/*
- * 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_ */
--- /dev/null
+ao_product.h
+telelco*.elf
--- /dev/null
+#
+# 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:
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=telelco-v0.2
+include $(TOPDIR)/stm/Makefile-flash.defs
--- /dev/null
+/*
+ * 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_ */
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],
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;
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]);
ao_pins.h \
ao_product.h \
ao_task.h \
+ ao_adc_fast.h \
stm32f0.h
#
ao_boot_chain.c \
ao_cmd.c \
ao_usb_stm.c \
+ ao_trng.c \
ao_task.c \
ao_product.c
#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_ */
#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)
{
ao_usb_init();
- ao_cmd_register(usbtrng_cmds);
+ ao_trng_init();
+
ao_led_off(AO_LED_RED);
ao_start_scheduler();
-#!/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 = {
.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',
.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",
};
{
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");
}
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
JTabbedPane pane;
- AltosUIMap map;
+ AltosUIMapNew map;
TeleGPSInfo gps_info;
TeleGPSState gps_state;
AltosInfoTable info_table;
}
void load_maps() {
- new AltosUIMapPreload(this);
+ new AltosUIMapPreloadNew(this);
}
void disconnect() {
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) {
bag = getContentPane();
bag.setLayout(new GridBagLayout());
- GridBagConstraints c = new GridBagConstraints();
-
setTitle("TeleGPS");
menu_bar = new JMenuBar();
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>();
/* 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);
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);
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");
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;
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 {
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
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;
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;
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() {
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;
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();
}
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) {
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();
+ }
}
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 {
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;
JTabbedPane pane;
AltosGraph graph;
AltosUIEnable enable;
- AltosUIMap map;
+ AltosUIMapNew map;
AltosState state;
AltosFlightStats stats;
AltosGraphDataSet graphDataSet;
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);
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 {
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
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 {
}
}
+ 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());
add(new FirmwareVersion(this, y++));
add(new FlightLogMax(this, y++));
add(new BatteryVoltage(this, y++));
+ add(new ReceiverBattery(this, y++));
}
}
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;
package org.altusmetrum.telegps;
import java.awt.event.*;
-import org.altusmetrum.altoslib_6.*;
+import org.altusmetrum.altoslib_8.*;
public class TeleGPSStatusUpdate implements ActionListener {
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"
SetOutPath $INSTDIR
- File "../icon/${WIN_APP_EXE}"
File "../icon/${WIN_TELEM_EXE}"
File "../icon/${WIN_EEPROM_EXE}"
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"
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"